/*-------------------------------------------------------------------------
 *
 * parse_expr.c
 *	  handle expressions in parser
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/parser/parse_expr.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "parser/analyze.h"
#include "parser/parse_agg.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
#include "utils/xml.h"
#ifdef FDD
#include "utils/guc.h"
#endif

/* GUC 参数 */
bool		Transform_null_equals = false;

#ifdef FDD
#define FDB_ENABLE_POSTGIS_HOOK_CONF "fdb.enable_fbasegis"
fdb_postgis_expr_hook_type fdb_postgis_hook = NULL;
bool		EnableFbaseGis;

static char *fdb_get_full_func_name(FuncCall *fc_func_info);
#endif

static Node *fc_transformExprRecurse(ParseState *fc_pstate, Node *fc_expr);
static Node *fc_transformParamRef(ParseState *fc_pstate, ParamRef *fc_pref);
static Node *fc_transformAExprOp(ParseState *fc_pstate, A_Expr *fc_a);
static Node *fc_transformAExprOpAny(ParseState *fc_pstate, A_Expr *fc_a);
static Node *fc_transformAExprOpAll(ParseState *fc_pstate, A_Expr *fc_a);
static Node *fc_transformAExprDistinct(ParseState *fc_pstate, A_Expr *fc_a);
static Node *fc_transformAExprNullIf(ParseState *fc_pstate, A_Expr *fc_a);
static Node *fc_transformAExprIn(ParseState *fc_pstate, A_Expr *fc_a);
static Node *fc_transformAExprBetween(ParseState *fc_pstate, A_Expr *fc_a);
static Node *fc_transformBoolExpr(ParseState *fc_pstate, BoolExpr *fc_a);
static Node *fc_transformFuncCall(ParseState *fc_pstate, FuncCall *fc_fn);
static Node *fc_transformMultiAssignRef(ParseState *fc_pstate, MultiAssignRef *fc_maref);
static Node *fc_transformCaseExpr(ParseState *fc_pstate, CaseExpr *fc_c);
static Node *fc_transformSubLink(ParseState *fc_pstate, SubLink *fc_sublink);
static Node *fc_transformArrayExpr(ParseState *fc_pstate, A_ArrayExpr *fc_a,
								Oid fc_array_type, Oid fc_element_type, int32 fc_typmod);
static Node *fc_transformRowExpr(ParseState *fc_pstate, RowExpr *fc_r, bool fc_allowDefault);
static Node *fc_transformCoalesceExpr(ParseState *fc_pstate, CoalesceExpr *fc_c);
static Node *fc_transformMinMaxExpr(ParseState *fc_pstate, MinMaxExpr *fc_m);
static Node *fc_transformSQLValueFunction(ParseState *fc_pstate,
									   SQLValueFunction *fc_svf);
static Node *fc_transformXmlExpr(ParseState *fc_pstate, XmlExpr *fc_x);
static Node *fc_transformXmlSerialize(ParseState *fc_pstate, XmlSerialize *fc_xs);
static Node *fc_transformBooleanTest(ParseState *fc_pstate, BooleanTest *fc_b);
static Node *fc_transformCurrentOfExpr(ParseState *fc_pstate, CurrentOfExpr *fc_cexpr);
static Node *fc_transformColumnRef(ParseState *fc_pstate, ColumnRef *fc_cref);
static Node *fc_transformWholeRowRef(ParseState *fc_pstate,
								  ParseNamespaceItem *fc_nsitem,
								  int fc_sublevels_up, int fc_location);
static Node *fc_transformIndirection(ParseState *fc_pstate, A_Indirection *fc_ind);
static Node *fc_transformTypeCast(ParseState *fc_pstate, TypeCast *fc_tc);
static Node *fc_transformCollateClause(ParseState *fc_pstate, CollateClause *fc_c);
static Node *fc_make_row_comparison_op(ParseState *fc_pstate, List *fc_opname,
									List *fc_largs, List *fc_rargs, int fc_location);
static Node *fc_make_row_distinct_op(ParseState *fc_pstate, List *fc_opname,
								  RowExpr *fc_lrow, RowExpr *fc_rrow, int fc_location);
static Expr *fc_make_distinct_op(ParseState *fc_pstate, List *fc_opname,
							  Node *fc_ltree, Node *fc_rtree, int fc_location);
static Node *fc_make_nulltest_from_distinct(ParseState *fc_pstate,
										 A_Expr *fc_distincta, Node *fc_arg);


#ifdef FDD
void FDD_Update_EnableFbaseGis_Conf(void)
{
	char *fc_conf_str = GetConfigOptionByName(FDB_ENABLE_POSTGIS_HOOK_CONF, NULL, true);
	EnableFbaseGis = false;
	if (fc_conf_str != NULL && strcmp(fc_conf_str, "on") == 0)
	{
		EnableFbaseGis = true;
	}
}


char*  fdb_get_current_search_path(void)
{
	List	   *fc_search_path = fetch_search_path(false);
	char *fc_ns_name = NULL;
	if (fc_search_path != NULL)
	{
		Oid fc_schemaOid = linitial_oid(fc_search_path);
		fc_ns_name =  get_namespace_name(fc_schemaOid);
	}

	return fc_ns_name;
}


#endif

/*
 * transformExpr -
 *	  分析和转换表达式。类型检查和类型转换在
 *	  这里完成。此处理将原始语法输出转换为
 *	  具有完全确定语义的表达式树。
 */
Node * transformExpr(ParseState *fc_pstate, Node *fc_expr, ParseExprKind fc_exprKind)
{
	Node	   *fc_result;
	ParseExprKind fc_sv_expr_kind;

	/* 保存和恢复我们正在解析的表达式类型的身份 */
	Assert(fc_exprKind != EXPR_KIND_NONE);
	fc_sv_expr_kind = fc_pstate->p_expr_kind;
	fc_pstate->p_expr_kind = fc_exprKind;

	fc_result = fc_transformExprRecurse(fc_pstate, fc_expr);

	fc_pstate->p_expr_kind = fc_sv_expr_kind;

	return fc_result;
}
#ifdef FDD
static char *fdb_get_full_func_name(FuncCall *fc_func_info)
{
	if (list_length(fc_func_info->funcname) == 1)
	{
		//只带了函数名字，没有带其他的
		char *fc_fun_ns = fdb_get_current_search_path();

		if (fc_fun_ns != NULL)
		{
			StringInfo fc_result_str = makeStringInfo();
			String *fc_fun_name = (String*)lfirst(list_head(fc_func_info->funcname));
			appendStringInfo(fc_result_str, "%s.%s", fc_fun_ns, fc_fun_name->sval);

			return fc_result_str->data;
		}

		//获取不到namespace，通过后面直接返回转换的
	}

	return NameListToString(fc_func_info->funcname);
}

#endif

static Node * fc_transformExprRecurse(ParseState *fc_pstate, Node *fc_expr)
{
	Node	   *fc_result;

	if (fc_expr == NULL)
		return NULL;

	/* 防止因过于复杂的表达式导致栈溢出 */
	check_stack_depth();

	switch (nodeTag(fc_expr))
	{
		case T_ColumnRef:
			fc_result = fc_transformColumnRef(fc_pstate, (ColumnRef *) fc_expr);
			break;

		case T_ParamRef:
			fc_result = fc_transformParamRef(fc_pstate, (ParamRef *) fc_expr);
			break;

		case T_A_Const:
			fc_result = (Node *) make_const(fc_pstate, (A_Const *) fc_expr);
			break;

		case T_A_Indirection:
			fc_result = fc_transformIndirection(fc_pstate, (A_Indirection *) fc_expr);
			break;

		case T_A_ArrayExpr:
			fc_result = fc_transformArrayExpr(fc_pstate, (A_ArrayExpr *) fc_expr,
										InvalidOid, InvalidOid, -1);
			break;

		case T_TypeCast:
			fc_result = fc_transformTypeCast(fc_pstate, (TypeCast *) fc_expr);
			break;

		case T_CollateClause:
			fc_result = fc_transformCollateClause(fc_pstate, (CollateClause *) fc_expr);
			break;

		case T_A_Expr:
			{
				A_Expr	   *fc_a = (A_Expr *) fc_expr;

				switch (fc_a->kind)
				{
					case AEXPR_OP:
						fc_result = fc_transformAExprOp(fc_pstate, fc_a);
						break;
					case AEXPR_OP_ANY:
						fc_result = fc_transformAExprOpAny(fc_pstate, fc_a);
						break;
					case AEXPR_OP_ALL:
						fc_result = fc_transformAExprOpAll(fc_pstate, fc_a);
						break;
					case AEXPR_DISTINCT:
					case AEXPR_NOT_DISTINCT:
						fc_result = fc_transformAExprDistinct(fc_pstate, fc_a);
						break;
					case AEXPR_NULLIF:
						fc_result = fc_transformAExprNullIf(fc_pstate, fc_a);
						break;
					case AEXPR_IN:
						fc_result = fc_transformAExprIn(fc_pstate, fc_a);
						break;
					case AEXPR_LIKE:
					case AEXPR_ILIKE:
					case AEXPR_SIMILAR:
						/* 我们可以像 AEXPR_OP 一样转换这些 */
						fc_result = fc_transformAExprOp(fc_pstate, fc_a);
						break;
					case AEXPR_BETWEEN:
					case AEXPR_NOT_BETWEEN:
					case AEXPR_BETWEEN_SYM:
					case AEXPR_NOT_BETWEEN_SYM:
						fc_result = fc_transformAExprBetween(fc_pstate, fc_a);
						break;
					default:
						elog(ERROR, "unrecognized A_Expr kind: %d", fc_a->kind);
						fc_result = NULL;	/* 保持编译器安静 */
						break;
				}
				break;
			}

		case T_BoolExpr:
			fc_result = fc_transformBoolExpr(fc_pstate, (BoolExpr *) fc_expr);
			break;

		case T_FuncCall:
#ifdef FDD
			{
				if (EnableFbaseGis)
				{
					#define  FDB_fdd_func_prefix ("fbase_gis.fdb_")
					//开关开启，需要判断是否是fdb函数
					char *fc_funcname  = fdb_get_full_func_name((FuncCall *) fc_expr);
					
					if(strstr(fc_funcname, FDB_fdd_func_prefix) != NULL)
					{
						Node *fc_changed_node = NULL;
						bool fc_surport = false;
						if (fdb_postgis_hook != NULL)
						{
							fc_changed_node = fdb_postgis_hook(fc_pstate, fc_expr, &fc_surport);
						}

						if (fc_changed_node != NULL)
						{
							//已经替换成any表达式或者 false表达式
							if (nodeTag(fc_changed_node) == T_A_Const)
							{
								fc_result = (Node *) make_const(fc_pstate, (A_Const *) fc_changed_node);
							}
							else
							{
								fc_result = fc_transformAExprOpAny(fc_pstate, (A_Expr *) fc_changed_node);
							}
							
							//break跳出switch块
							break;
						}
						else if (fc_surport)
						{
							//支持转换，但是转换失败，报错，退出
							ereport(ERROR, (errmsg("fail to change func %s to fbase gis procedure, please check log.", fc_funcname)));
						}
					}
				}

				//不符合转换条件，或者转换失败但是又没报错（异常），继续走
				fc_result = fc_transformFuncCall(fc_pstate, (FuncCall *) fc_expr);
				break;
			}
#else
			fc_result = fc_transformFuncCall(fc_pstate, (FuncCall *) fc_expr);
			break;
#endif
		case T_MultiAssignRef:
			fc_result = fc_transformMultiAssignRef(fc_pstate, (MultiAssignRef *) fc_expr);
			break;

		case T_GroupingFunc:
			fc_result = transformGroupingFunc(fc_pstate, (GroupingFunc *) fc_expr);
			break;

		case T_NamedArgExpr:
			{
				NamedArgExpr *fc_na = (NamedArgExpr *) fc_expr;

				fc_na->arg = (Expr *) fc_transformExprRecurse(fc_pstate, (Node *) fc_na->arg);
				fc_result = fc_expr;
				break;
			}

		case T_SubLink:
			fc_result = fc_transformSubLink(fc_pstate, (SubLink *) fc_expr);
			break;

		case T_CaseExpr:
			fc_result = fc_transformCaseExpr(fc_pstate, (CaseExpr *) fc_expr);
			break;

		case T_RowExpr:
			fc_result = fc_transformRowExpr(fc_pstate, (RowExpr *) fc_expr, false);
			break;

		case T_CoalesceExpr:
			fc_result = fc_transformCoalesceExpr(fc_pstate, (CoalesceExpr *) fc_expr);
			break;

		case T_MinMaxExpr:
			fc_result = fc_transformMinMaxExpr(fc_pstate, (MinMaxExpr *) fc_expr);
			break;

		case T_SQLValueFunction:
			fc_result = fc_transformSQLValueFunction(fc_pstate,
											   (SQLValueFunction *) fc_expr);
			break;

		case T_XmlExpr:
			fc_result = fc_transformXmlExpr(fc_pstate, (XmlExpr *) fc_expr);
			break;

		case T_XmlSerialize:
			fc_result = fc_transformXmlSerialize(fc_pstate, (XmlSerialize *) fc_expr);
			break;

		case T_NullTest:
			{
				NullTest   *fc_n = (NullTest *) fc_expr;

				fc_n->arg = (Expr *) fc_transformExprRecurse(fc_pstate, (Node *) fc_n->arg);
				/* 参数可以是任何类型，因此不要强制转换 */
				fc_n->argisrow = type_is_rowtype(exprType((Node *) fc_n->arg));
				fc_result = fc_expr;
				break;
			}

		case T_BooleanTest:
			fc_result = fc_transformBooleanTest(fc_pstate, (BooleanTest *) fc_expr);
			break;

		case T_CurrentOfExpr:
			fc_result = fc_transformCurrentOfExpr(fc_pstate, (CurrentOfExpr *) fc_expr);
			break;

			/*
			 * 在所有 DEFAULT 合法的地方，调用者应该已经
			 * 处理它，而不是将其传递给 transformExpr()。
			 */
		case T_SetToDefault:
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("DEFAULT is not allowed in this context"),
					 parser_errposition(fc_pstate,
										((SetToDefault *) fc_expr)->location)));
			break;

			/*
			 * CaseTestExpr 不需要任何处理；它仅
			 * 以完全形成的状态注入到解析树中。
			 *
			 * 通常我们这里不应该看到 Var，但对于 transformJoinUsingClause()
			 * 来说，创建包含已经转换的 Vars 的未转换操作符
			 * 树是方便的。最佳替代方案是拆解和重建列
			 * 引用，这似乎是昂贵而无意义的。所以允许它。
			 */
		case T_CaseTestExpr:
		case T_Var:
			{
				fc_result = (Node *) fc_expr;
				break;
			}

		default:
			/* 不应到达这里 */
			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(fc_expr));
			fc_result = NULL;		/* 保持编译器安静 */
			break;
	}

	return fc_result;
}

/*
 * 辅助例程，用于传递“列不存在”的错误消息
 *
 * （通常我们不需要这么辛苦，但从任意节点进行字段
 * 选择的一般情况需要它。）
 */
static void fc_unknown_attribute(ParseState *fc_pstate, Node *fc_relref, const char *fc_attname,
				  int fc_location)
{
	RangeTblEntry *fc_rte;

	if (IsA(fc_relref, Var) &&
		((Var *) fc_relref)->varattno == InvalidAttrNumber)
	{
		/* 通过别名引用 RTE，而不是实际表名 */
		fc_rte = GetRTEByRangeTablePosn(fc_pstate,
									 ((Var *) fc_relref)->varno,
									 ((Var *) fc_relref)->varlevelsup);
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_COLUMN),
				 errmsg("column %s.%s does not exist",
						fc_rte->eref->aliasname, fc_attname),
				 parser_errposition(fc_pstate, fc_location)));
	}
	else
	{
		/* 必须通过引用表达式的类型来执行此操作 */
		Oid			fc_relTypeId = exprType(fc_relref);

		if (ISCOMPLEX(fc_relTypeId))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_COLUMN),
					 errmsg("column \"%s\" not found in data type %s",
							fc_attname, format_type_be(fc_relTypeId)),
					 parser_errposition(fc_pstate, fc_location)));
		else if (fc_relTypeId == RECORDOID)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_COLUMN),
					 errmsg("could not identify column \"%s\" in record data type",
							fc_attname),
					 parser_errposition(fc_pstate, fc_location)));
		else
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("column notation .%s applied to type %s, "
							"which is not a composite type",
							fc_attname, format_type_be(fc_relTypeId)),
					 parser_errposition(fc_pstate, fc_location)));
	}
}

static Node * fc_transformIndirection(ParseState *fc_pstate, A_Indirection *fc_ind)
{
	Node	   *fc_last_srf = fc_pstate->p_last_srf;
	Node	   *fc_result = fc_transformExprRecurse(fc_pstate, fc_ind->arg);
	List	   *fc_subscripts = NIL;
	int			fc_location = exprLocation(fc_result);
	ListCell   *fc_i;

	/*
	 * 我们必须将任何字段选择操作与下标化操作分开。相邻的 A_Indices 节点必须视为单个多维下标操作。
	 */
	foreach(fc_i, fc_ind->indirection)
	{
		Node	   *fc_n = lfirst(fc_i);

		if (IsA(fc_n, A_Indices))
			fc_subscripts = lappend(fc_subscripts, fc_n);
		else if (IsA(fc_n, A_Star))
		{
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("row expansion via \"*\" is not supported here"),
					 parser_errposition(fc_pstate, fc_location)));
		}
		else
		{
			Node	   *fc_newresult;

			Assert(IsA(fc_n, String));

			/* 在此字段选择之前处理下标 */
			if (fc_subscripts)
				fc_result = (Node *) transformContainerSubscripts(fc_pstate,
															   fc_result,
															   exprType(fc_result),
															   exprTypmod(fc_result),
															   fc_subscripts,
															   false);
			fc_subscripts = NIL;

			fc_newresult = ParseFuncOrColumn(fc_pstate,
										  list_make1(fc_n),
										  list_make1(fc_result),
										  fc_last_srf,
										  NULL,
										  false,
										  fc_location);
			if (fc_newresult == NULL)
				fc_unknown_attribute(fc_pstate, fc_result, strVal(fc_n), fc_location);
			fc_result = fc_newresult;
		}
	}
	/* 处理尾随下标（如果有） */
	if (fc_subscripts)
		fc_result = (Node *) transformContainerSubscripts(fc_pstate,
													   fc_result,
													   exprType(fc_result),
													   exprTypmod(fc_result),
													   fc_subscripts,
													   false);

	return fc_result;
}

/*
 * 转换 ColumnRef。
 *
 * 如果你发现自己在修改这些代码，请查看 ExpandColumnRefStar。
 */
static Node * fc_transformColumnRef(ParseState *fc_pstate, ColumnRef *fc_cref)
{
	Node	   *fc_node = NULL;
	char	   *fc_nspname = NULL;
	char	   *fc_relname = NULL;
	char	   *fc_colname = NULL;
	ParseNamespaceItem *fc_nsitem;
	int			fc_levels_up;
	enum
	{
		CRERR_NO_COLUMN,
		CRERR_NO_RTE,
		CRERR_WRONG_DB,
		CRERR_TOO_MANY
	}			fc_crerr = CRERR_NO_COLUMN;
	const char *fc_err;

	/*
	 * 检查列引用是否在查询中的无效位置。我们允许在大多数地方使用列引用，
	 * 除了默认表达式和分区边界表达式。
	 */
	fc_err = NULL;
	switch (fc_pstate->p_expr_kind)
	{
		case EXPR_KIND_NONE:
			Assert(false);		/* 不能发生 */
			break;
		case EXPR_KIND_OTHER:
		case EXPR_KIND_JOIN_ON:
		case EXPR_KIND_JOIN_USING:
		case EXPR_KIND_FROM_SUBSELECT:
		case EXPR_KIND_FROM_FUNCTION:
		case EXPR_KIND_WHERE:
		case EXPR_KIND_POLICY:
		case EXPR_KIND_HAVING:
		case EXPR_KIND_FILTER:
		case EXPR_KIND_WINDOW_PARTITION:
		case EXPR_KIND_WINDOW_ORDER:
		case EXPR_KIND_WINDOW_FRAME_RANGE:
		case EXPR_KIND_WINDOW_FRAME_ROWS:
		case EXPR_KIND_WINDOW_FRAME_GROUPS:
		case EXPR_KIND_SELECT_TARGET:
		case EXPR_KIND_INSERT_TARGET:
		case EXPR_KIND_UPDATE_SOURCE:
		case EXPR_KIND_UPDATE_TARGET:
		case EXPR_KIND_MERGE_WHEN:
		case EXPR_KIND_GROUP_BY:
		case EXPR_KIND_ORDER_BY:
		case EXPR_KIND_DISTINCT_ON:
		case EXPR_KIND_LIMIT:
		case EXPR_KIND_OFFSET:
		case EXPR_KIND_RETURNING:
		case EXPR_KIND_VALUES:
		case EXPR_KIND_VALUES_SINGLE:
		case EXPR_KIND_CHECK_CONSTRAINT:
		case EXPR_KIND_DOMAIN_CHECK:
		case EXPR_KIND_FUNCTION_DEFAULT:
		case EXPR_KIND_INDEX_EXPRESSION:
		case EXPR_KIND_INDEX_PREDICATE:
		case EXPR_KIND_STATS_EXPRESSION:
		case EXPR_KIND_ALTER_COL_TRANSFORM:
		case EXPR_KIND_EXECUTE_PARAMETER:
		case EXPR_KIND_TRIGGER_WHEN:
		case EXPR_KIND_PARTITION_EXPRESSION:
		case EXPR_KIND_CALL_ARGUMENT:
		case EXPR_KIND_COPY_WHERE:
		case EXPR_KIND_GENERATED_COLUMN:
		case EXPR_KIND_CYCLE_MARK:
			/* 可以 */
			break;

		case EXPR_KIND_COLUMN_DEFAULT:
			fc_err = _("cannot use column reference in DEFAULT expression");
			break;
		case EXPR_KIND_PARTITION_BOUND:
			fc_err = _("cannot use column reference in partition bound expression");
			break;

			/*
			 * 这里故意没有默认情况，以便编译器会警告如果我们添加一个新的 ParseExprKind 而没有扩展这个开关。如果我们在运行时看到一个未被识别的值，行为将与 EXPR_KIND_OTHER 相同，这在任何情况下都是合理的。
			 */
	}
	if (fc_err)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg_internal("%s", fc_err),
				 parser_errposition(fc_pstate, fc_cref->location)));

	/*
	 * 让 PreParseColumnRefHook 尝试一下，如果它返回非空值， 那就没事了。
	 */
	if (fc_pstate->p_pre_columnref_hook != NULL)
	{
		fc_node = fc_pstate->p_pre_columnref_hook(fc_pstate, fc_cref);
		if (fc_node != NULL)
			return fc_node;
	}

	/*----------
	 * 允许的语法有：
	 *
	 * A		首先尝试解析为未限定的列名；
	 *			如果没有成功，尝试解析为未限定的表名 (A.*)。
	 * A.B		A 是未限定的表名；B 是列或函数名（优先尝试列名）。
	 * A.B.C	模式 A，表 B，列或函数名 C。
	 * A.B.C.D	目录 A，模式 B，表 C，列或函数 D。
	 * A.*		A 是未限定的表名；表示整行值。
	 * A.B.*	模式 A 中表 B 的整行值。
	 * A.B.C.*	目录 A 中模式 B 中表 C 的整行值。
	 *
	 * 我们不需要处理裸 "*"；它仅在 SELECT 列表的顶层被语法接受，并且 transformTargetList
	 * 会在到达这里之前处理它。此外，"A.*" 等将在 SELECT 顶层出现时由 transformTargetList
	 * 展开，所以在这里我们只会将它们视为函数或操作符输入。
	 *
	 * 当前，如果给定了目录名，则必须与当前数据库名相等；我们在这里检查它，然后丢弃它。
	 *----------
	 */
	switch (list_length(fc_cref->fields))
	{
		case 1:
			{
				Node	   *fc_field1 = (Node *) linitial(fc_cref->fields);

				Assert(IsA(fc_field1, String));
				fc_colname = strVal(fc_field1);

				/* 尝试识别为未限定列 */
				fc_node = colNameToVar(fc_pstate, fc_colname, false, fc_cref->location);

				if (fc_node == NULL)
				{
					/*
					 * 在任何范围表项中未被识别为列。
					 *
					 * 尝试将名称识别为关系。注意，只有已经进入范围表的
					 * 关系才会被识别。
					 *
					 * 这是为了与 PostQUEL 灵感的语法保持向后兼容。这
					 * 种语法现在的首选形式是 "rel.*"。
					 */
					fc_nsitem = refnameNamespaceItem(fc_pstate, NULL, fc_colname,
												  fc_cref->location,
												  &fc_levels_up);
					if (fc_nsitem)
						fc_node = fc_transformWholeRowRef(fc_pstate, fc_nsitem, fc_levels_up,
													fc_cref->location);
				}
				break;
			}
		case 2:
			{
				Node	   *fc_field1 = (Node *) linitial(fc_cref->fields);
				Node	   *fc_field2 = (Node *) lsecond(fc_cref->fields);

				Assert(IsA(fc_field1, String));
				fc_relname = strVal(fc_field1);

				/* 定位引用的 nsitem */
				fc_nsitem = refnameNamespaceItem(fc_pstate, fc_nspname, fc_relname,
											  fc_cref->location,
											  &fc_levels_up);
				if (fc_nsitem == NULL)
				{
					fc_crerr = CRERR_NO_RTE;
					break;
				}

				/* 整行引用？ */
				if (IsA(fc_field2, A_Star))
				{
					fc_node = fc_transformWholeRowRef(fc_pstate, fc_nsitem, fc_levels_up,
												fc_cref->location);
					break;
				}

				Assert(IsA(fc_field2, String));
				fc_colname = strVal(fc_field2);

				/* 尝试识别为 nsitem 的列 */
				fc_node = scanNSItemForColumn(fc_pstate, fc_nsitem, fc_levels_up, fc_colname,
										   fc_cref->location);
				if (fc_node == NULL)
				{
					/* 尝试作为整个行的函数调用 */
					fc_node = fc_transformWholeRowRef(fc_pstate, fc_nsitem, fc_levels_up,
												fc_cref->location);
					fc_node = ParseFuncOrColumn(fc_pstate,
											 list_make1(makeString(fc_colname)),
											 list_make1(fc_node),
											 fc_pstate->p_last_srf,
											 NULL,
											 false,
											 fc_cref->location);
				}
				break;
			}
		case 3:
			{
				Node	   *fc_field1 = (Node *) linitial(fc_cref->fields);
				Node	   *fc_field2 = (Node *) lsecond(fc_cref->fields);
				Node	   *fc_field3 = (Node *) lthird(fc_cref->fields);

				Assert(IsA(fc_field1, String));
				fc_nspname = strVal(fc_field1);
				Assert(IsA(fc_field2, String));
				fc_relname = strVal(fc_field2);

				/* 定位引用的 nsitem */
				fc_nsitem = refnameNamespaceItem(fc_pstate, fc_nspname, fc_relname,
											  fc_cref->location,
											  &fc_levels_up);
				if (fc_nsitem == NULL)
				{
					fc_crerr = CRERR_NO_RTE;
					break;
				}

				/* 整行引用？ */
				if (IsA(fc_field3, A_Star))
				{
					fc_node = fc_transformWholeRowRef(fc_pstate, fc_nsitem, fc_levels_up,
												fc_cref->location);
					break;
				}

				Assert(IsA(fc_field3, String));
				fc_colname = strVal(fc_field3);

				/* 尝试识别为 nsitem 的列 */
				fc_node = scanNSItemForColumn(fc_pstate, fc_nsitem, fc_levels_up, fc_colname,
										   fc_cref->location);
				if (fc_node == NULL)
				{
					/* 尝试作为整个行的函数调用 */
					fc_node = fc_transformWholeRowRef(fc_pstate, fc_nsitem, fc_levels_up,
												fc_cref->location);
					fc_node = ParseFuncOrColumn(fc_pstate,
											 list_make1(makeString(fc_colname)),
											 list_make1(fc_node),
											 fc_pstate->p_last_srf,
											 NULL,
											 false,
											 fc_cref->location);
				}
				break;
			}
		case 4:
			{
				Node	   *fc_field1 = (Node *) linitial(fc_cref->fields);
				Node	   *fc_field2 = (Node *) lsecond(fc_cref->fields);
				Node	   *fc_field3 = (Node *) lthird(fc_cref->fields);
				Node	   *fc_field4 = (Node *) lfourth(fc_cref->fields);
				char	   *fc_catname;

				Assert(IsA(fc_field1, String));
				fc_catname = strVal(fc_field1);
				Assert(IsA(fc_field2, String));
				fc_nspname = strVal(fc_field2);
				Assert(IsA(fc_field3, String));
				fc_relname = strVal(fc_field3);

				/*
				 * 我们检查目录名，然后忽略它。
				 */
				if (strcmp(fc_catname, get_database_name(MyDatabaseId)) != 0)
				{
					fc_crerr = CRERR_WRONG_DB;
					break;
				}

				/* 定位引用的 nsitem */
				fc_nsitem = refnameNamespaceItem(fc_pstate, fc_nspname, fc_relname,
											  fc_cref->location,
											  &fc_levels_up);
				if (fc_nsitem == NULL)
				{
					fc_crerr = CRERR_NO_RTE;
					break;
				}

				/* 整行引用？ */
				if (IsA(fc_field4, A_Star))
				{
					fc_node = fc_transformWholeRowRef(fc_pstate, fc_nsitem, fc_levels_up,
												fc_cref->location);
					break;
				}

				Assert(IsA(fc_field4, String));
				fc_colname = strVal(fc_field4);

				/* 尝试识别为 nsitem 的列 */
				fc_node = scanNSItemForColumn(fc_pstate, fc_nsitem, fc_levels_up, fc_colname,
										   fc_cref->location);
				if (fc_node == NULL)
				{
					/* 尝试作为整个行的函数调用 */
					fc_node = fc_transformWholeRowRef(fc_pstate, fc_nsitem, fc_levels_up,
												fc_cref->location);
					fc_node = ParseFuncOrColumn(fc_pstate,
											 list_make1(makeString(fc_colname)),
											 list_make1(fc_node),
											 fc_pstate->p_last_srf,
											 NULL,
											 false,
											 fc_cref->location);
				}
				break;
			}
		default:
			fc_crerr = CRERR_TOO_MANY; /* 名字太多点号 */
			break;
	}

	/*
	 * 现在给 PostParseColumnRefHook 一个机会（如果有的话）。我们传递
	 * 到目前为止的翻译，以便如果它希望的话可以在有冲突的 ColumnRef 解析时抛出错误。
	 * （如果它无论如何都进行翻译，我们将抛出错误，因为我们无法撤销前面的步骤对 pstate
	 * 可能产生的影响。）如果它返回 NULL，则使用标准翻译，或者如果没有标准翻译则抛出合适的错误。
	 */
	if (fc_pstate->p_post_columnref_hook != NULL)
	{
		Node	   *fc_hookresult;

		fc_hookresult = fc_pstate->p_post_columnref_hook(fc_pstate, fc_cref, fc_node);
		if (fc_node == NULL)
			fc_node = fc_hookresult;
		else if (fc_hookresult != NULL)
			ereport(ERROR,
					(errcode(ERRCODE_AMBIGUOUS_COLUMN),
					 errmsg("column reference \"%s\" is ambiguous",
							NameListToString(fc_cref->fields)),
					 parser_errposition(fc_pstate, fc_cref->location)));
	}

	/*
	 * 如果未找到翻译则抛出错误。
	 */
	if (fc_node == NULL)
	{
		switch (fc_crerr)
		{
			case CRERR_NO_COLUMN:
				errorMissingColumn(fc_pstate, fc_relname, fc_colname, fc_cref->location);
				break;
			case CRERR_NO_RTE:
				errorMissingRTE(fc_pstate, makeRangeVar(fc_nspname, fc_relname,
													 fc_cref->location));
				break;
			case CRERR_WRONG_DB:
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("cross-database references are not implemented: %s",
								NameListToString(fc_cref->fields)),
						 parser_errposition(fc_pstate, fc_cref->location)));
				break;
			case CRERR_TOO_MANY:
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("improper qualified name (too many dotted names): %s",
								NameListToString(fc_cref->fields)),
						 parser_errposition(fc_pstate, fc_cref->location)));
				break;
		}
	}

	return fc_node;
}

static Node * fc_transformParamRef(ParseState *fc_pstate, ParamRef *fc_pref)
{
	Node	   *fc_result;

	/*
	 * 核心解析器对 Params 一无所知。如果提供了钩子，则调用它。
	 * 如果没有，或者钩子返回 NULL，则抛出通用错误。
	 */
	if (fc_pstate->p_paramref_hook != NULL)
		fc_result = fc_pstate->p_paramref_hook(fc_pstate, fc_pref);
	else
		fc_result = NULL;

	if (fc_result == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_PARAMETER),
				 errmsg("there is no parameter $%d", fc_pref->number),
				 parser_errposition(fc_pstate, fc_pref->location)));

	return fc_result;
}

/* 测试 a_expr 是否为普通的 NULL 常量 */
static bool fc_exprIsNullConstant(Node *fc_arg)
{
	if (fc_arg && IsA(fc_arg, A_Const))
	{
		A_Const    *fc_con = (A_Const *) fc_arg;

		if (fc_con->isnull)
			return true;
	}
	return false;
}

static Node * fc_transformAExprOp(ParseState *fc_pstate, A_Expr *fc_a)
{
	Node	   *fc_lexpr = fc_a->lexpr;
	Node	   *fc_rexpr = fc_a->rexpr;
	Node	   *fc_result;

	/*
	 * 针对兼容性处理 "foo = NULL" 和 "NULL = foo"，以适应标准被破坏的产品
	 * （如 Microsoft 的产品）。将它们转换为 IS NULL 表达式。
	 * （如果任一侧是 CaseTestExpr，则该表达式是从 CASE-WHEN 表达式内部生成，
	 * transform_null_equals 不适用。）
	 */
	if (Transform_null_equals &&
		list_length(fc_a->name) == 1 &&
		strcmp(strVal(linitial(fc_a->name)), "=") == 0 &&
		(fc_exprIsNullConstant(fc_lexpr) || fc_exprIsNullConstant(fc_rexpr)) &&
		(!IsA(fc_lexpr, CaseTestExpr) && !IsA(fc_rexpr, CaseTestExpr)))
	{
		NullTest   *fc_n = makeNode(NullTest);

		fc_n->nulltesttype = IS_NULL;
		fc_n->location = fc_a->location;

		if (fc_exprIsNullConstant(fc_lexpr))
			fc_n->arg = (Expr *) fc_rexpr;
		else
			fc_n->arg = (Expr *) fc_lexpr;

		fc_result = fc_transformExprRecurse(fc_pstate, (Node *) fc_n);
	}
	else if (fc_lexpr && IsA(fc_lexpr, RowExpr) &&
			 fc_rexpr && IsA(fc_rexpr, SubLink) &&
			 ((SubLink *) fc_rexpr)->subLinkType == EXPR_SUBLINK)
	{
		/*
		 * 将 "row op subselect" 转换为 ROWCOMPARE 子链接。以前文法执行此操作，
		 * 但既然在表达式的任何地方允许使用行构造，现在在这里处理更容易。
		 */
		SubLink    *fc_s = (SubLink *) fc_rexpr;

		fc_s->subLinkType = ROWCOMPARE_SUBLINK;
		fc_s->testexpr = fc_lexpr;
		fc_s->operName = fc_a->name;
		fc_s->location = fc_a->location;
		fc_result = fc_transformExprRecurse(fc_pstate, (Node *) fc_s);
	}
	else if (fc_lexpr && IsA(fc_lexpr, RowExpr) &&
			 fc_rexpr && IsA(fc_rexpr, RowExpr))
	{
		/* ROW() op ROW() 被特别处理 */
		fc_lexpr = fc_transformExprRecurse(fc_pstate, fc_lexpr);
		fc_rexpr = fc_transformExprRecurse(fc_pstate, fc_rexpr);

		fc_result = fc_make_row_comparison_op(fc_pstate,
										fc_a->name,
										castNode(RowExpr, fc_lexpr)->args,
										castNode(RowExpr, fc_rexpr)->args,
										fc_a->location);
	}
	else
	{
		/* 普通标量操作符 */
		Node	   *fc_last_srf = fc_pstate->p_last_srf;

		fc_lexpr = fc_transformExprRecurse(fc_pstate, fc_lexpr);
		fc_rexpr = fc_transformExprRecurse(fc_pstate, fc_rexpr);

		fc_result = (Node *) make_op(fc_pstate,
								  fc_a->name,
								  fc_lexpr,
								  fc_rexpr,
								  fc_last_srf,
								  fc_a->location);
	}

	return fc_result;
}

static Node * fc_transformAExprOpAny(ParseState *fc_pstate, A_Expr *fc_a)
{
	Node	   *fc_lexpr = fc_transformExprRecurse(fc_pstate, fc_a->lexpr);
	Node	   *fc_rexpr = fc_transformExprRecurse(fc_pstate, fc_a->rexpr);

	return (Node *) make_scalar_array_op(fc_pstate,
										 fc_a->name,
										 true,
										 fc_lexpr,
										 fc_rexpr,
										 fc_a->location);
}

static Node * fc_transformAExprOpAll(ParseState *fc_pstate, A_Expr *fc_a)
{
	Node	   *fc_lexpr = fc_transformExprRecurse(fc_pstate, fc_a->lexpr);
	Node	   *fc_rexpr = fc_transformExprRecurse(fc_pstate, fc_a->rexpr);

	return (Node *) make_scalar_array_op(fc_pstate,
										 fc_a->name,
										 false,
										 fc_lexpr,
										 fc_rexpr,
										 fc_a->location);
}

static Node * fc_transformAExprDistinct(ParseState *fc_pstate, A_Expr *fc_a)
{
	Node	   *fc_lexpr = fc_a->lexpr;
	Node	   *fc_rexpr = fc_a->rexpr;
	Node	   *fc_result;

	/*
	 * 如果任一输入是未装饰的 NULL 字面量，则转换为另一个输入的 NullTest。
	 * 比完整的 DistinctExpr 更容易处理，避免需要要求数据类型具有 "=" 操作符。
	 */
	if (fc_exprIsNullConstant(fc_rexpr))
		return fc_make_nulltest_from_distinct(fc_pstate, fc_a, fc_lexpr);
	if (fc_exprIsNullConstant(fc_lexpr))
		return fc_make_nulltest_from_distinct(fc_pstate, fc_a, fc_rexpr);

	fc_lexpr = fc_transformExprRecurse(fc_pstate, fc_lexpr);
	fc_rexpr = fc_transformExprRecurse(fc_pstate, fc_rexpr);

	if (fc_lexpr && IsA(fc_lexpr, RowExpr) &&
		fc_rexpr && IsA(fc_rexpr, RowExpr))
	{
		/* ROW() op ROW() 被特别处理 */
		fc_result = fc_make_row_distinct_op(fc_pstate, fc_a->name,
									  (RowExpr *) fc_lexpr,
									  (RowExpr *) fc_rexpr,
									  fc_a->location);
	}
	else
	{
		/* 普通标量操作符 */
		fc_result = (Node *) fc_make_distinct_op(fc_pstate,
										   fc_a->name,
										   fc_lexpr,
										   fc_rexpr,
										   fc_a->location);
	}

	/*
	 * 如果不是 DISTINCT，我们首先构建一个 DistinctExpr，然后在其上添加一个 NOT。
	 */
	if (fc_a->kind == AEXPR_NOT_DISTINCT)
		fc_result = (Node *) makeBoolExpr(NOT_EXPR,
									   list_make1(fc_result),
									   fc_a->location);

	return fc_result;
}

static Node * fc_transformAExprNullIf(ParseState *fc_pstate, A_Expr *fc_a)
{
	Node	   *fc_lexpr = fc_transformExprRecurse(fc_pstate, fc_a->lexpr);
	Node	   *fc_rexpr = fc_transformExprRecurse(fc_pstate, fc_a->rexpr);
	OpExpr	   *fc_result;

	fc_result = (OpExpr *) make_op(fc_pstate,
								fc_a->name,
								fc_lexpr,
								fc_rexpr,
								fc_pstate->p_last_srf,
								fc_a->location);

	/*
	 * 比较运算符本身应返回布尔值...
	 */
	if (fc_result->opresulttype != BOOLOID)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("NULLIF requires = operator to yield boolean"),
				 parser_errposition(fc_pstate, fc_a->location)));
	if (fc_result->opretset)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
		/* 翻译：%s 是 SQL 构造的名称，例如 NULLIF */
				 errmsg("%s must not return a set", "NULLIF"),
				 parser_errposition(fc_pstate, fc_a->location)));

	/*
	 * ... 但 NullIfExpr 将返回第一个操作数的类型。
	 */
	fc_result->opresulttype = exprType((Node *) linitial(fc_result->args));

	/*
	 * 我们依赖于 NullIfExpr 和 OpExpr 是相同结构
	 */
	NodeSetTag(fc_result, T_NullIfExpr);

	return (Node *) fc_result;
}

static Node * fc_transformAExprIn(ParseState *fc_pstate, A_Expr *fc_a)
{
	Node	   *fc_result = NULL;
	Node	   *fc_lexpr;
	List	   *fc_rexprs;
	List	   *fc_rvars;
	List	   *fc_rnonvars;
	bool		fc_useOr;
	ListCell   *fc_l;

	/*
	 * 如果操作符是 <>, 则应结合使用 AND 而不是 OR。
	 */
	if (strcmp(strVal(linitial(fc_a->name)), "<>") == 0)
		fc_useOr = false;
	else
		fc_useOr = true;

	
	fc_lexpr = fc_transformExprRecurse(fc_pstate, fc_a->lexpr);
	fc_rexprs = fc_rvars = fc_rnonvars = NIL;
	foreach(fc_l, (List *) fc_a->rexpr)
	{
		Node	   *fc_rexpr = fc_transformExprRecurse(fc_pstate, lfirst(fc_l));

		fc_rexprs = lappend(fc_rexprs, fc_rexpr);
		if (contain_vars_of_level(fc_rexpr, 0))
			fc_rvars = lappend(fc_rvars, fc_rexpr);
		else
			fc_rnonvars = lappend(fc_rnonvars, fc_rexpr);
	}

	/*
	 * ScalarArrayOpExpr 仅在有多个非 Var 右侧项时才有用。
	 */
	if (list_length(fc_rnonvars) > 1)
	{
		List	   *fc_allexprs;
		Oid			fc_scalar_type;
		Oid			fc_array_type;

		/*
		 * 尝试为数组元素选择一个通用类型。 注意，由于 LHS 的类型在列表中是第一个，因此在有疑问时（例如，当所有 RHS 项都是未知字面量时），它将被优先考虑。
		 *
		 * 注意：这里使用 list_concat 而不是 lcons，以避免损坏 rnonvars。
		 */
		fc_allexprs = list_concat(list_make1(fc_lexpr), fc_rnonvars);
		fc_scalar_type = select_common_type(fc_pstate, fc_allexprs, NULL, NULL);

		/* 我们必须验证所选类型实际上是可行的 */
		if (OidIsValid(fc_scalar_type) &&
			!verify_common_type(fc_scalar_type, fc_allexprs))
			fc_scalar_type = InvalidOid;

		/*
		 * 我们是否有可以使用的数组类型？ 除非没有数组类型，否则在共同类型为 RECORD 时我们不需要冒险使用 ScalarArrayOpExpr，因为下面的 RowExpr 比较逻辑可以处理一些非相同行类型的情况。
		 */
		if (OidIsValid(fc_scalar_type) && fc_scalar_type != RECORDOID)
			fc_array_type = get_array_type(fc_scalar_type);
		else
			fc_array_type = InvalidOid;
		if (fc_array_type != InvalidOid)
		{
			/*
			 * 好的：将所有右侧的非 Var 输入强制转换为通用类型，并为它们构建一个 ArrayExpr。
			 */
			List	   *fc_aexprs;
			ArrayExpr  *fc_newa;

			fc_aexprs = NIL;
			foreach(fc_l, fc_rnonvars)
			{
				Node	   *fc_rexpr = (Node *) lfirst(fc_l);

				fc_rexpr = coerce_to_common_type(fc_pstate, fc_rexpr,
											  fc_scalar_type,
											  "IN");
				fc_aexprs = lappend(fc_aexprs, fc_rexpr);
			}
			fc_newa = makeNode(ArrayExpr);
			fc_newa->array_typeid = fc_array_type;
			/* array_collid 将由 parse_collate.c 设置 */
			fc_newa->element_typeid = fc_scalar_type;
			fc_newa->elements = fc_aexprs;
			fc_newa->multidims = false;
			fc_newa->location = -1;

			fc_result = (Node *) make_scalar_array_op(fc_pstate,
												   fc_a->name,
												   fc_useOr,
												   fc_lexpr,
												   (Node *) fc_newa,
												   fc_a->location);

			/* 在下面的循环中仅考虑 Vars（如果有的话） */
			fc_rexprs = fc_rvars;
		}
	}

	/*
	 * 必须用困难的方式来做，即用布尔表达式树。
	 */
	foreach(fc_l, fc_rexprs)
	{
		Node	   *fc_rexpr = (Node *) lfirst(fc_l);
		Node	   *fc_cmp;

		if (IsA(fc_lexpr, RowExpr) &&
			IsA(fc_rexpr, RowExpr))
		{
			/* ROW() op ROW() 被特别处理 */
			fc_cmp = fc_make_row_comparison_op(fc_pstate,
										 fc_a->name,
										 copyObject(((RowExpr *) fc_lexpr)->args),
										 ((RowExpr *) fc_rexpr)->args,
										 fc_a->location);
		}
		else
		{
			/* 普通标量操作符 */
			fc_cmp = (Node *) make_op(fc_pstate,
								   fc_a->name,
								   copyObject(fc_lexpr),
								   fc_rexpr,
								   fc_pstate->p_last_srf,
								   fc_a->location);
		}

		fc_cmp = coerce_to_boolean(fc_pstate, fc_cmp, "IN");
		if (fc_result == NULL)
			fc_result = fc_cmp;
		else
			fc_result = (Node *) makeBoolExpr(fc_useOr ? OR_EXPR : AND_EXPR,
										   list_make2(fc_result, fc_cmp),
										   fc_a->location);
	}

	return fc_result;
}

static Node * fc_transformAExprBetween(ParseState *fc_pstate, A_Expr *fc_a)
{
	Node	   *fc_aexpr;
	Node	   *fc_bexpr;
	Node	   *fc_cexpr;
	Node	   *fc_result;
	Node	   *fc_sub1;
	Node	   *fc_sub2;
	List	   *fc_args;

	/* 将 A_Expr 解构为三个子表达式 */
	fc_aexpr = fc_a->lexpr;
	fc_args = castNode(List, fc_a->rexpr);
	Assert(list_length(fc_args) == 2);
	fc_bexpr = (Node *) linitial(fc_args);
	fc_cexpr = (Node *) lsecond(fc_args);

	/*
	 * 构建等效的比较表达式。 为安全起见，复制多次引用的子表达式。 （XXX 这实际上是错误的，因为这会导致对可能是易变表达式的多个运行时评估……）
	 *
	 * 理想情况下，我们希望不使用硬编码的运算符，而是使用操作类。然而，混合数据类型和其他问题使这变得困难：
	 * http://archives.postgresql.org/pgsql-hackers/2008-08/msg01142.php
	 */
	switch (fc_a->kind)
	{
		case AEXPR_BETWEEN:
			fc_args = list_make2(makeSimpleA_Expr(AEXPR_OP, ">=",
											   fc_aexpr, fc_bexpr,
											   fc_a->location),
							  makeSimpleA_Expr(AEXPR_OP, "<=",
											   copyObject(fc_aexpr), fc_cexpr,
											   fc_a->location));
			fc_result = (Node *) makeBoolExpr(AND_EXPR, fc_args, fc_a->location);
			break;
		case AEXPR_NOT_BETWEEN:
			fc_args = list_make2(makeSimpleA_Expr(AEXPR_OP, "<",
											   fc_aexpr, fc_bexpr,
											   fc_a->location),
							  makeSimpleA_Expr(AEXPR_OP, ">",
											   copyObject(fc_aexpr), fc_cexpr,
											   fc_a->location));
			fc_result = (Node *) makeBoolExpr(OR_EXPR, fc_args, fc_a->location);
			break;
		case AEXPR_BETWEEN_SYM:
			fc_args = list_make2(makeSimpleA_Expr(AEXPR_OP, ">=",
											   fc_aexpr, fc_bexpr,
											   fc_a->location),
							  makeSimpleA_Expr(AEXPR_OP, "<=",
											   copyObject(fc_aexpr), fc_cexpr,
											   fc_a->location));
			fc_sub1 = (Node *) makeBoolExpr(AND_EXPR, fc_args, fc_a->location);
			fc_args = list_make2(makeSimpleA_Expr(AEXPR_OP, ">=",
											   copyObject(fc_aexpr), copyObject(fc_cexpr),
											   fc_a->location),
							  makeSimpleA_Expr(AEXPR_OP, "<=",
											   copyObject(fc_aexpr), copyObject(fc_bexpr),
											   fc_a->location));
			fc_sub2 = (Node *) makeBoolExpr(AND_EXPR, fc_args, fc_a->location);
			fc_args = list_make2(fc_sub1, fc_sub2);
			fc_result = (Node *) makeBoolExpr(OR_EXPR, fc_args, fc_a->location);
			break;
		case AEXPR_NOT_BETWEEN_SYM:
			fc_args = list_make2(makeSimpleA_Expr(AEXPR_OP, "<",
											   fc_aexpr, fc_bexpr,
											   fc_a->location),
							  makeSimpleA_Expr(AEXPR_OP, ">",
											   copyObject(fc_aexpr), fc_cexpr,
											   fc_a->location));
			fc_sub1 = (Node *) makeBoolExpr(OR_EXPR, fc_args, fc_a->location);
			fc_args = list_make2(makeSimpleA_Expr(AEXPR_OP, "<",
											   copyObject(fc_aexpr), copyObject(fc_cexpr),
											   fc_a->location),
							  makeSimpleA_Expr(AEXPR_OP, ">",
											   copyObject(fc_aexpr), copyObject(fc_bexpr),
											   fc_a->location));
			fc_sub2 = (Node *) makeBoolExpr(OR_EXPR, fc_args, fc_a->location);
			fc_args = list_make2(fc_sub1, fc_sub2);
			fc_result = (Node *) makeBoolExpr(AND_EXPR, fc_args, fc_a->location);
			break;
		default:
			elog(ERROR, "unrecognized A_Expr kind: %d", fc_a->kind);
			fc_result = NULL;		/* 保持编译器安静 */
			break;
	}

	return fc_transformExprRecurse(fc_pstate, fc_result);
}

static Node * fc_transformBoolExpr(ParseState *fc_pstate, BoolExpr *fc_a)
{
	List	   *fc_args = NIL;
	const char *fc_opname;
	ListCell   *fc_lc;

	switch (fc_a->boolop)
	{
		case AND_EXPR:
			fc_opname = "AND";
			break;
		case OR_EXPR:
			fc_opname = "OR";
			break;
		case NOT_EXPR:
			fc_opname = "NOT";
			break;
		default:
			elog(ERROR, "unrecognized boolop: %d", (int) fc_a->boolop);
			fc_opname = NULL;		/* 保持编译器安静 */
			break;
	}

	foreach(fc_lc, fc_a->args)
	{
		Node	   *fc_arg = (Node *) lfirst(fc_lc);

		fc_arg = fc_transformExprRecurse(fc_pstate, fc_arg);
		fc_arg = coerce_to_boolean(fc_pstate, fc_arg, fc_opname);
		fc_args = lappend(fc_args, fc_arg);
	}

	return (Node *) makeBoolExpr(fc_a->boolop, fc_args, fc_a->location);
}

static Node * fc_transformFuncCall(ParseState *fc_pstate, FuncCall *fc_fn)
{
	Node	   *fc_last_srf = fc_pstate->p_last_srf;
	List	   *fc_targs;
	ListCell   *fc_args;

	/* 转换参数列表... */
	fc_targs = NIL;
	foreach(fc_args, fc_fn->args)
	{
		fc_targs = lappend(fc_targs, fc_transformExprRecurse(fc_pstate,
													(Node *) lfirst(fc_args)));
	}

	/*
	 * 当使用 WITHIN GROUP 时，我们将其 ORDER BY 表达式视为函数的其他参数，用于函数查找和参数类型强制转换。因此，转换每个这样的表达式并将其添加到 targs 列表中。我们没有明确标记每个参数来自何处，但 ParseFuncOrColumn 可以通过参考 list_length(fn->agg_order) 来判断各自的来源。
	 */
	if (fc_fn->agg_within_group)
	{
		Assert(fc_fn->agg_order != NIL);
		foreach(fc_args, fc_fn->agg_order)
		{
			SortBy	   *fc_arg = (SortBy *) lfirst(fc_args);

			fc_targs = lappend(fc_targs, transformExpr(fc_pstate, fc_arg->node,
												 EXPR_KIND_ORDER_BY));
		}
	}

	/* ... 然后交给 ParseFuncOrColumn */
	return ParseFuncOrColumn(fc_pstate,
							 fc_fn->funcname,
							 fc_targs,
							 fc_last_srf,
							 fc_fn,
							 false,
							 fc_fn->location);
}

static Node * fc_transformMultiAssignRef(ParseState *fc_pstate, MultiAssignRef *fc_maref)
{
	SubLink    *fc_sublink;
	RowExpr    *fc_rexpr;
	Query	   *fc_qtree;
	TargetEntry *fc_tle;

	/* 我们只应该在 UPDATE tlists 的第一阶段处理中看到这一点 */
	Assert(fc_pstate->p_expr_kind == EXPR_KIND_UPDATE_SOURCE);

	/* 只有在这是第一列时，我们才需要转换源 */
	if (fc_maref->colno == 1)
	{
		/*
		 * 目前，我们只允许 EXPR SubLinks 和 RowExprs 作为 UPDATE 多重赋值的源。这足以覆盖有趣的情况；在最坏的情况下，某人必须写（SELECT * FROM expr）来展开另一种形式的复合返回表达式。
		 */
		if (IsA(fc_maref->source, SubLink) &&
			((SubLink *) fc_maref->source)->subLinkType == EXPR_SUBLINK)
		{
			/* 将其重新标记为 MULTIEXPR_SUBLINK */
			fc_sublink = (SubLink *) fc_maref->source;
			fc_sublink->subLinkType = MULTIEXPR_SUBLINK;
			/* 并进行转换 */
			fc_sublink = (SubLink *) fc_transformExprRecurse(fc_pstate,
													   (Node *) fc_sublink);

			fc_qtree = castNode(Query, fc_sublink->subselect);

			/* 检查子查询返回所需的列数 */
			if (count_nonjunk_tlist_entries(fc_qtree->targetList) != fc_maref->ncolumns)
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("number of columns does not match number of values"),
						 parser_errposition(fc_pstate, fc_sublink->location)));

			/*
			 * 构建一个包含 MULTIEXPR SubLink 的 resjunk tlist 项，并将其添加到 pstate->p_multiassign_exprs，稍后将其附加到完成的目标列表中。我们无需担心为其选择一个 resno；transformUpdateStmt 会处理这个。
			 */
			fc_tle = makeTargetEntry((Expr *) fc_sublink, 0, NULL, true);
			fc_pstate->p_multiassign_exprs = lappend(fc_pstate->p_multiassign_exprs,
												  fc_tle);

			/*
			 * 为 MULTIEXPR SubLink 分配一个在此目标列表中的唯一 ID。我们可以直接使用它在 p_multiassign_exprs 列表中的位置。
			 */
			fc_sublink->subLinkId = list_length(fc_pstate->p_multiassign_exprs);
		}
		else if (IsA(fc_maref->source, RowExpr))
		{
			/* 转换 RowExpr，允许 SetToDefault 项 */
			fc_rexpr = (RowExpr *) fc_transformRowExpr(fc_pstate,
												 (RowExpr *) fc_maref->source,
												 true);

			/* 检查它返回所需的列数 */
			if (list_length(fc_rexpr->args) != fc_maref->ncolumns)
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("number of columns does not match number of values"),
						 parser_errposition(fc_pstate, fc_rexpr->location)));

			/*
			 * 暂时将其附加到 p_multiassign_exprs，以便在我们回来时可以获取它以获取额外的列。
			 */
			fc_tle = makeTargetEntry((Expr *) fc_rexpr, 0, NULL, true);
			fc_pstate->p_multiassign_exprs = lappend(fc_pstate->p_multiassign_exprs,
												  fc_tle);
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression"),
					 parser_errposition(fc_pstate, exprLocation(fc_maref->source))));
	}
	else
	{
		/*
		 * 多重赋值中的第二列或后续列。重新获取转换后的 SubLink 或 RowExpr，我们假设它仍然是 p_multiassign_exprs 中的最后一个条目。
		 */
		Assert(fc_pstate->p_multiassign_exprs != NIL);
		fc_tle = (TargetEntry *) llast(fc_pstate->p_multiassign_exprs);
	}

	/*
	 * 为当前列发出适当的输出表达式
	 */
	if (IsA(fc_tle->expr, SubLink))
	{
		Param	   *fc_param;

		fc_sublink = (SubLink *) fc_tle->expr;
		Assert(fc_sublink->subLinkType == MULTIEXPR_SUBLINK);
		fc_qtree = castNode(Query, fc_sublink->subselect);

		/* 构建一个表示当前子查询输出列的 Param */
		fc_tle = (TargetEntry *) list_nth(fc_qtree->targetList, fc_maref->colno - 1);
		Assert(!fc_tle->resjunk);

		fc_param = makeNode(Param);
		fc_param->paramkind = PARAM_MULTIEXPR;
		fc_param->paramid = (fc_sublink->subLinkId << 16) | fc_maref->colno;
		fc_param->paramtype = exprType((Node *) fc_tle->expr);
		fc_param->paramtypmod = exprTypmod((Node *) fc_tle->expr);
		fc_param->paramcollid = exprCollation((Node *) fc_tle->expr);
		fc_param->location = exprLocation((Node *) fc_tle->expr);

		return (Node *) fc_param;
	}

	if (IsA(fc_tle->expr, RowExpr))
	{
		Node	   *fc_result;

		fc_rexpr = (RowExpr *) fc_tle->expr;

		/* 仅提取并返回 RowExpr 的下一个元素 */
		fc_result = (Node *) list_nth(fc_rexpr->args, fc_maref->colno - 1);

		/*
		 * 如果我们在最后一列，从 p_multiassign_exprs 中删除 RowExpr；我们不再需要它，并且不想将其放入完成的 UPDATE tlist 中。我们假设这仍然是 p_multiassign_exprs 中的最后一个条目。
		 */
		if (fc_maref->colno == fc_maref->ncolumns)
			fc_pstate->p_multiassign_exprs =
				list_delete_last(fc_pstate->p_multiassign_exprs);

		return fc_result;
	}

	elog(ERROR, "unexpected expr type in multiassign list");
	return NULL;				/* 保持编译器安静 */
}

static Node * fc_transformCaseExpr(ParseState *fc_pstate, CaseExpr *fc_c)
{
	CaseExpr   *fc_newc = makeNode(CaseExpr);
	Node	   *fc_last_srf = fc_pstate->p_last_srf;
	Node	   *fc_arg;
	CaseTestExpr *fc_placeholder;
	List	   *fc_newargs;
	List	   *fc_resultexprs;
	ListCell   *fc_l;
	Node	   *fc_defresult;
	Oid			fc_ptype;

	/* 如果有，转换测试表达式 */
	fc_arg = fc_transformExprRecurse(fc_pstate, (Node *) fc_c->arg);

	/* 生成测试表达式的占位符 */
	if (fc_arg)
	{
		/*
		 * 如果测试表达式是一个无类型字面量，将其强制转换为文本。我们现在必须执行某些操作，因为在占位符上无法进行这种强制转换。这没有之前的 7.4 及更早版本那样灵活，但足以处理常见的愚蠢编码。
		 */
		if (exprType(fc_arg) == UNKNOWNOID)
			fc_arg = coerce_to_common_type(fc_pstate, fc_arg, TEXTOID, "CASE");

		/*
		 * 对测试表达式执行排序分配，以便我们知道要用什么排序标记占位符。原则上我们可以把它留给 parse_collate.c 后续来处理，但传播结果到 CaseTestExpr 会过于复杂。
		 */
		assign_expr_collations(fc_pstate, fc_arg);

		fc_placeholder = makeNode(CaseTestExpr);
		fc_placeholder->typeId = exprType(fc_arg);
		fc_placeholder->typeMod = exprTypmod(fc_arg);
		fc_placeholder->collation = exprCollation(fc_arg);
	}
	else
		fc_placeholder = NULL;

	fc_newc->arg = (Expr *) fc_arg;

	/* 转换参数列表 */
	fc_newargs = NIL;
	fc_resultexprs = NIL;
	foreach(fc_l, fc_c->args)
	{
		CaseWhen   *fc_w = lfirst_node(CaseWhen, fc_l);
		CaseWhen   *fc_neww = makeNode(CaseWhen);
		Node	   *fc_warg;

		fc_warg = (Node *) fc_w->expr;
		if (fc_placeholder)
		{
			/* 指定了简写形式，因此展开... */
			fc_warg = (Node *) makeSimpleA_Expr(AEXPR_OP, "=",
											 (Node *) fc_placeholder,
											 fc_warg,
											 fc_w->location);
		}
		fc_neww->expr = (Expr *) fc_transformExprRecurse(fc_pstate, fc_warg);

		fc_neww->expr = (Expr *) coerce_to_boolean(fc_pstate,
												(Node *) fc_neww->expr,
												"CASE/WHEN");

		fc_warg = (Node *) fc_w->result;
		fc_neww->result = (Expr *) fc_transformExprRecurse(fc_pstate, fc_warg);
		fc_neww->location = fc_w->location;

		fc_newargs = lappend(fc_newargs, fc_neww);
		fc_resultexprs = lappend(fc_resultexprs, fc_neww->result);
	}

	fc_newc->args = fc_newargs;

	/* 转换默认子句 */
	fc_defresult = (Node *) fc_c->defresult;
	if (fc_defresult == NULL)
	{
		A_Const    *fc_n = makeNode(A_Const);

		fc_n->isnull = true;
		fc_n->location = -1;
		fc_defresult = (Node *) fc_n;
	}
	fc_newc->defresult = (Expr *) fc_transformExprRecurse(fc_pstate, fc_defresult);

	/*
	 * 注意：默认结果被视为确定首选类型时最重要的类型。这就是代码之前是如何工作的，但我觉得这有点不合理 --- tgl
	 */
	fc_resultexprs = lcons(fc_newc->defresult, fc_resultexprs);

	fc_ptype = select_common_type(fc_pstate, fc_resultexprs, "CASE", NULL);
	Assert(OidIsValid(fc_ptype));
	fc_newc->casetype = fc_ptype;
	/* casecollid 将由 parse_collate.c 设置 */

	/* 如有必要，转换默认结果子句 */
	fc_newc->defresult = (Expr *)
		coerce_to_common_type(fc_pstate,
							  (Node *) fc_newc->defresult,
							  fc_ptype,
							  "CASE/ELSE");

	/* 如有必要，转换 when 子句结果 */
	foreach(fc_l, fc_newc->args)
	{
		CaseWhen   *fc_w = (CaseWhen *) lfirst(fc_l);

		fc_w->result = (Expr *)
			coerce_to_common_type(fc_pstate,
								  (Node *) fc_w->result,
								  fc_ptype,
								  "CASE/WHEN");
	}

	/* 如果任何子表达式包含 SRF，则抱怨 */
	if (fc_pstate->p_last_srf != fc_last_srf)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
		/* 翻译者：%s 是 SQL 构造的名称，例如 GROUP BY */
				 errmsg("set-returning functions are not allowed in %s",
						"CASE"),
				 errhint("You might be able to move the set-returning function into a LATERAL FROM item."),
				 parser_errposition(fc_pstate,
									exprLocation(fc_pstate->p_last_srf))));

	fc_newc->location = fc_c->location;

	return (Node *) fc_newc;
}

static Node * fc_transformSubLink(ParseState *fc_pstate, SubLink *fc_sublink)
{
	Node	   *fc_result = (Node *) fc_sublink;
	Query	   *fc_qtree;
	const char *fc_err;

	/*
	 * 检查子链接是否在查询中的无效位置。我们允许在 SELECT/INSERT/UPDATE/DELETE/MERGE 中使用子链接，但通常不允许在实用语句中。
	 */
	fc_err = NULL;
	switch (fc_pstate->p_expr_kind)
	{
		case EXPR_KIND_NONE:
			Assert(false);		/* 不能发生 */
			break;
		case EXPR_KIND_OTHER:
			/* 这里接受子链接；调用者必须在需要时抛出错误 */
			break;
		case EXPR_KIND_JOIN_ON:
		case EXPR_KIND_JOIN_USING:
		case EXPR_KIND_FROM_SUBSELECT:
		case EXPR_KIND_FROM_FUNCTION:
		case EXPR_KIND_WHERE:
		case EXPR_KIND_POLICY:
		case EXPR_KIND_HAVING:
		case EXPR_KIND_FILTER:
		case EXPR_KIND_WINDOW_PARTITION:
		case EXPR_KIND_WINDOW_ORDER:
		case EXPR_KIND_WINDOW_FRAME_RANGE:
		case EXPR_KIND_WINDOW_FRAME_ROWS:
		case EXPR_KIND_WINDOW_FRAME_GROUPS:
		case EXPR_KIND_SELECT_TARGET:
		case EXPR_KIND_INSERT_TARGET:
		case EXPR_KIND_UPDATE_SOURCE:
		case EXPR_KIND_UPDATE_TARGET:
		case EXPR_KIND_MERGE_WHEN:
		case EXPR_KIND_GROUP_BY:
		case EXPR_KIND_ORDER_BY:
		case EXPR_KIND_DISTINCT_ON:
		case EXPR_KIND_LIMIT:
		case EXPR_KIND_OFFSET:
		case EXPR_KIND_RETURNING:
		case EXPR_KIND_VALUES:
		case EXPR_KIND_VALUES_SINGLE:
		case EXPR_KIND_CYCLE_MARK:
			/* 可以 */
			break;
		case EXPR_KIND_CHECK_CONSTRAINT:
		case EXPR_KIND_DOMAIN_CHECK:
			fc_err = _("cannot use subquery in check constraint");
			break;
		case EXPR_KIND_COLUMN_DEFAULT:
		case EXPR_KIND_FUNCTION_DEFAULT:
			fc_err = _("cannot use subquery in DEFAULT expression");
			break;
		case EXPR_KIND_INDEX_EXPRESSION:
			fc_err = _("cannot use subquery in index expression");
			break;
		case EXPR_KIND_INDEX_PREDICATE:
			fc_err = _("cannot use subquery in index predicate");
			break;
		case EXPR_KIND_STATS_EXPRESSION:
			fc_err = _("cannot use subquery in statistics expression");
			break;
		case EXPR_KIND_ALTER_COL_TRANSFORM:
			fc_err = _("cannot use subquery in transform expression");
			break;
		case EXPR_KIND_EXECUTE_PARAMETER:
			fc_err = _("cannot use subquery in EXECUTE parameter");
			break;
		case EXPR_KIND_TRIGGER_WHEN:
			fc_err = _("cannot use subquery in trigger WHEN condition");
			break;
		case EXPR_KIND_PARTITION_BOUND:
			fc_err = _("cannot use subquery in partition bound");
			break;
		case EXPR_KIND_PARTITION_EXPRESSION:
			fc_err = _("cannot use subquery in partition key expression");
			break;
		case EXPR_KIND_CALL_ARGUMENT:
			fc_err = _("cannot use subquery in CALL argument");
			break;
		case EXPR_KIND_COPY_WHERE:
			fc_err = _("cannot use subquery in COPY FROM WHERE condition");
			break;
		case EXPR_KIND_GENERATED_COLUMN:
			fc_err = _("cannot use subquery in column generation expression");
			break;

			/*
			 * 这里故意没有默认情况，以便编译器会警告如果我们添加一个新的 ParseExprKind 而没有扩展这个开关。如果我们在运行时看到一个未被识别的值，行为将与 EXPR_KIND_OTHER 相同，这在任何情况下都是合理的。
			 */
	}
	if (fc_err)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg_internal("%s", fc_err),
				 parser_errposition(fc_pstate, fc_sublink->location)));

	fc_pstate->p_hasSubLinks = true;

	/*
	 * 好的，让我们转换子SELECT。
	 */
	fc_qtree = parse_sub_analyze(fc_sublink->subselect, fc_pstate, NULL, false, true);

	/*
	 * 检查我们得到了一个 SELECT。如果文法的限制条件给
	 * 其它情况是不可能的，但还是要检查一下。
	 */
	if (!IsA(fc_qtree, Query) ||
		fc_qtree->commandType != CMD_SELECT)
		elog(ERROR, "unexpected non-SELECT command in SubLink");

	fc_sublink->subselect = (Node *) fc_qtree;

	if (fc_sublink->subLinkType == EXISTS_SUBLINK)
	{
		/*
		 * EXISTS 不需要测试表达式或组合运算符。这些字段应该已经是 null，但要确保。
		 */
		fc_sublink->testexpr = NULL;
		fc_sublink->operName = NIL;
	}
	else if (fc_sublink->subLinkType == EXPR_SUBLINK ||
			 fc_sublink->subLinkType == ARRAY_SUBLINK)
	{
		/*
		 * 确保子选择返回一个单列（忽略 resjunk 目标）。
		 */
		if (count_nonjunk_tlist_entries(fc_qtree->targetList) != 1)
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("subquery must return only one column"),
					 parser_errposition(fc_pstate, fc_sublink->location)));

		/*
		 * EXPR 和 ARRAY 不需要测试表达式或组合运算符。这些字段应该已经是 null，但要确保。
		 */
		fc_sublink->testexpr = NULL;
		fc_sublink->operName = NIL;
	}
	else if (fc_sublink->subLinkType == MULTIEXPR_SUBLINK)
	{
		/* 与 EXPR 情况相同，除了对列数没有限制 */
		fc_sublink->testexpr = NULL;
		fc_sublink->operName = NIL;
	}
	else
	{
		/* ALL、ANY 或 ROWCOMPARE：生成行比较表达式 */
		Node	   *fc_lefthand;
		List	   *fc_left_list;
		List	   *fc_right_list;
		ListCell   *fc_l;

		/*
		 * 如果源是 "x IN (select)"，则转换为 "x = ANY (select)"。
		 */
		if (fc_sublink->operName == NIL)
			fc_sublink->operName = list_make1(makeString("="));

		/*
		 * 转换左侧表达式，并转换为列表
		 */
		fc_lefthand = fc_transformExprRecurse(fc_pstate, fc_sublink->testexpr);
		if (fc_lefthand && IsA(fc_lefthand, RowExpr))
			fc_left_list = ((RowExpr *) fc_lefthand)->args;
		else
			fc_left_list = list_make1(fc_lefthand);

		/*
		 * 构建一组 PARAM_SUBLINK 节点，表示子查询的输出列。
		 */
		fc_right_list = NIL;
		foreach(fc_l, fc_qtree->targetList)
		{
			TargetEntry *fc_tent = (TargetEntry *) lfirst(fc_l);
			Param	   *fc_param;

			if (fc_tent->resjunk)
				continue;

			fc_param = makeNode(Param);
			fc_param->paramkind = PARAM_SUBLINK;
			fc_param->paramid = fc_tent->resno;
			fc_param->paramtype = exprType((Node *) fc_tent->expr);
			fc_param->paramtypmod = exprTypmod((Node *) fc_tent->expr);
			fc_param->paramcollid = exprCollation((Node *) fc_tent->expr);
			fc_param->location = -1;

			fc_right_list = lappend(fc_right_list, fc_param);
		}

		/*
		 * 我们可以依赖 make_row_comparison_op 在列表长度不同的情况下进行抱怨，但我们更希望生成一个更具体的错误消息。
		 */
		if (list_length(fc_left_list) < list_length(fc_right_list))
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("subquery has too many columns"),
					 parser_errposition(fc_pstate, fc_sublink->location)));
		if (list_length(fc_left_list) > list_length(fc_right_list))
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("subquery has too few columns"),
					 parser_errposition(fc_pstate, fc_sublink->location)));

		/*
		 * 确定组合运算符并生成适当的行比较表达式。
		 */
		fc_sublink->testexpr = fc_make_row_comparison_op(fc_pstate,
												   fc_sublink->operName,
												   fc_left_list,
												   fc_right_list,
												   fc_sublink->location);
	}

	return fc_result;
}

/*
 * transformArrayExpr
 *
 * 如果调用者指定目标类型，生成的数组将完全是该类型。否则我们会尝试使用 select_common_type() 推断元素的公共类型。
 */
static Node * fc_transformArrayExpr(ParseState *fc_pstate, A_ArrayExpr *fc_a,
				   Oid fc_array_type, Oid fc_element_type, int32 fc_typmod)
{
	ArrayExpr  *fc_newa = makeNode(ArrayExpr);
	List	   *fc_newelems = NIL;
	List	   *fc_newcoercedelems = NIL;
	ListCell   *fc_element;
	Oid			fc_coerce_type;
	bool		fc_coerce_hard;

	/*
	 * 转换元素表达式
	 *
	 * 假设数组是一维的，除非我们发现一个数组类型的元素表达式。
	 */
	fc_newa->multidims = false;
	foreach(fc_element, fc_a->elements)
	{
		Node	   *fc_e = (Node *) lfirst(fc_element);
		Node	   *fc_newe;

		/*
		 * 如果一个元素本身是 A_ArrayExpr，则直接递归，以便我们可以传递任何给定的目标类型。
		 */
		if (IsA(fc_e, A_ArrayExpr))
		{
			fc_newe = fc_transformArrayExpr(fc_pstate,
									  (A_ArrayExpr *) fc_e,
									  fc_array_type,
									  fc_element_type,
									  fc_typmod);
			/* 我们这里肯定有一个数组 */
			Assert(fc_array_type == InvalidOid || fc_array_type == exprType(fc_newe));
			fc_newa->multidims = true;
		}
		else
		{
			fc_newe = fc_transformExprRecurse(fc_pstate, fc_e);

			/*
			 * 如果我们还没有找到一个，检查子数组表达式。
			 */
			if (!fc_newa->multidims && type_is_array(exprType(fc_newe)))
				fc_newa->multidims = true;
		}

		fc_newelems = lappend(fc_newelems, fc_newe);
	}

	/*
	 * 为元素选择目标类型。
	 *
	 * 如果我们没有获得目标数组类型，则必须尝试基于现有单个元素的类型推断公共类型。
	 */
	if (OidIsValid(fc_array_type))
	{
		/* 调用者必须确保 array_type 与 element_type 匹配 */
		Assert(OidIsValid(fc_element_type));
		fc_coerce_type = (fc_newa->multidims ? fc_array_type : fc_element_type);
		fc_coerce_hard = true;
	}
	else
	{
		/* 不能处理没有目标类型的空数组 */
		if (fc_newelems == NIL)
			ereport(ERROR,
					(errcode(ERRCODE_INDETERMINATE_DATATYPE),
					 errmsg("cannot determine type of empty array"),
					 errhint("Explicitly cast to the desired type, "
							 "for example ARRAY[]::integer[]."),
					 parser_errposition(fc_pstate, fc_a->location)));

		/* 为元素选择一个公共类型 */
		fc_coerce_type = select_common_type(fc_pstate, fc_newelems, "ARRAY", NULL);

		if (fc_newa->multidims)
		{
			fc_array_type = fc_coerce_type;
			fc_element_type = get_element_type(fc_array_type);
			if (!OidIsValid(fc_element_type))
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_OBJECT),
						 errmsg("could not find element type for data type %s",
								format_type_be(fc_array_type)),
						 parser_errposition(fc_pstate, fc_a->location)));
		}
		else
		{
			fc_element_type = fc_coerce_type;
			fc_array_type = get_array_type(fc_element_type);
			if (!OidIsValid(fc_array_type))
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_OBJECT),
						 errmsg("could not find array type for data type %s",
								format_type_be(fc_element_type)),
						 parser_errposition(fc_pstate, fc_a->location)));
		}
		fc_coerce_hard = false;
	}

	/*
	 * 强制元素转换为目标类型
	 *
	 * 如果数组被显式转换，则元素也相应地被显式强制转换。
	 *
	 * 如果数组的类型仅仅是从其元素的公共类型派生的，则元素隐式强制转换为公共类型。
	 * 这与 select_common_type() 的其他用法是一致的。
	 */
	foreach(fc_element, fc_newelems)
	{
		Node	   *fc_e = (Node *) lfirst(fc_element);
		Node	   *fc_newe;

		if (fc_coerce_hard)
		{
			fc_newe = coerce_to_target_type(fc_pstate, fc_e,
										 exprType(fc_e),
										 fc_coerce_type,
										 fc_typmod,
										 COERCION_EXPLICIT,
										 COERCE_EXPLICIT_CAST,
										 -1);
			if (fc_newe == NULL)
				ereport(ERROR,
						(errcode(ERRCODE_CANNOT_COERCE),
						 errmsg("cannot cast type %s to %s",
								format_type_be(exprType(fc_e)),
								format_type_be(fc_coerce_type)),
						 parser_errposition(fc_pstate, exprLocation(fc_e))));
		}
		else
			fc_newe = coerce_to_common_type(fc_pstate, fc_e,
										 fc_coerce_type,
										 "ARRAY");
		fc_newcoercedelems = lappend(fc_newcoercedelems, fc_newe);
	}

	fc_newa->array_typeid = fc_array_type;
	/* array_collid 将由 parse_collate.c 设置 */
	fc_newa->element_typeid = fc_element_type;
	fc_newa->elements = fc_newcoercedelems;
	fc_newa->location = fc_a->location;

	return (Node *) fc_newa;
}

static Node * fc_transformRowExpr(ParseState *fc_pstate, RowExpr *fc_r, bool fc_allowDefault)
{
	RowExpr    *fc_newr;
	char		fc_fname[16];
	int			fc_fnum;

	fc_newr = makeNode(RowExpr);

	/* 转换字段表达式 */
	fc_newr->args = transformExpressionList(fc_pstate, fc_r->args,
										 fc_pstate->p_expr_kind, fc_allowDefault);

	/* 不允许列数超过元组可容纳的数量 */
	if (list_length(fc_newr->args) > MaxTupleAttributeNumber)
		ereport(ERROR,
				(errcode(ERRCODE_TOO_MANY_COLUMNS),
				 errmsg("ROW expressions can have at most %d entries",
						MaxTupleAttributeNumber),
				 parser_errposition(fc_pstate, fc_r->location)));

	/* 除非后续转换，我们将类型视为 RECORD */
	fc_newr->row_typeid = RECORDOID;
	fc_newr->row_format = COERCE_IMPLICIT_CAST;

	/* ROW() 有匿名列，因此发明一些字段名称 */
	fc_newr->colnames = NIL;
	for (fc_fnum = 1; fc_fnum <= list_length(fc_newr->args); fc_fnum++)
	{
		snprintf(fc_fname, sizeof(fc_fname), "f%d", fc_fnum);
		fc_newr->colnames = lappend(fc_newr->colnames, makeString(pstrdup(fc_fname)));
	}

	fc_newr->location = fc_r->location;

	return (Node *) fc_newr;
}

static Node * fc_transformCoalesceExpr(ParseState *fc_pstate, CoalesceExpr *fc_c)
{
	CoalesceExpr *fc_newc = makeNode(CoalesceExpr);
	Node	   *fc_last_srf = fc_pstate->p_last_srf;
	List	   *fc_newargs = NIL;
	List	   *fc_newcoercedargs = NIL;
	ListCell   *fc_args;

	foreach(fc_args, fc_c->args)
	{
		Node	   *fc_e = (Node *) lfirst(fc_args);
		Node	   *fc_newe;

		fc_newe = fc_transformExprRecurse(fc_pstate, fc_e);
		fc_newargs = lappend(fc_newargs, fc_newe);
	}

	fc_newc->coalescetype = select_common_type(fc_pstate, fc_newargs, "COALESCE", NULL);
	/* coalescecollid 将由 parse_collate.c 设置 */

	/* 如有必要，转换参数 */
	foreach(fc_args, fc_newargs)
	{
		Node	   *fc_e = (Node *) lfirst(fc_args);
		Node	   *fc_newe;

		fc_newe = coerce_to_common_type(fc_pstate, fc_e,
									 fc_newc->coalescetype,
									 "COALESCE");
		fc_newcoercedargs = lappend(fc_newcoercedargs, fc_newe);
	}

	/* 如果任何子表达式包含 SRF，则抱怨 */
	if (fc_pstate->p_last_srf != fc_last_srf)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
		/* 翻译者：%s 是 SQL 构造的名称，例如 GROUP BY */
				 errmsg("set-returning functions are not allowed in %s",
						"COALESCE"),
				 errhint("You might be able to move the set-returning function into a LATERAL FROM item."),
				 parser_errposition(fc_pstate,
									exprLocation(fc_pstate->p_last_srf))));

	fc_newc->args = fc_newcoercedargs;
	fc_newc->location = fc_c->location;
	return (Node *) fc_newc;
}

static Node * fc_transformMinMaxExpr(ParseState *fc_pstate, MinMaxExpr *fc_m)
{
	MinMaxExpr *fc_newm = makeNode(MinMaxExpr);
	List	   *fc_newargs = NIL;
	List	   *fc_newcoercedargs = NIL;
	const char *fc_funcname = (fc_m->op == IS_GREATEST) ? "GREATEST" : "LEAST";
	ListCell   *fc_args;

	fc_newm->op = fc_m->op;
	foreach(fc_args, fc_m->args)
	{
		Node	   *fc_e = (Node *) lfirst(fc_args);
		Node	   *fc_newe;

		fc_newe = fc_transformExprRecurse(fc_pstate, fc_e);
		fc_newargs = lappend(fc_newargs, fc_newe);
	}

	fc_newm->minmaxtype = select_common_type(fc_pstate, fc_newargs, fc_funcname, NULL);
	/* minmaxcollid 和 inputcollid 将由 parse_collate.c 设置 */

	/* 如有必要，转换参数 */
	foreach(fc_args, fc_newargs)
	{
		Node	   *fc_e = (Node *) lfirst(fc_args);
		Node	   *fc_newe;

		fc_newe = coerce_to_common_type(fc_pstate, fc_e,
									 fc_newm->minmaxtype,
									 fc_funcname);
		fc_newcoercedargs = lappend(fc_newcoercedargs, fc_newe);
	}

	fc_newm->args = fc_newcoercedargs;
	fc_newm->location = fc_m->location;
	return (Node *) fc_newm;
}

static Node * fc_transformSQLValueFunction(ParseState *fc_pstate, SQLValueFunction *fc_svf)
{
	/*
	 * 我们需要做的就是插入正确的结果类型，并在需要时
	 * 验证 typmod，因此我们只需就地修改节点。
	 */
	switch (fc_svf->op)
	{
		case SVFOP_CURRENT_DATE:
			fc_svf->type = DATEOID;
			break;
		case SVFOP_CURRENT_TIME:
			fc_svf->type = TIMETZOID;
			break;
		case SVFOP_CURRENT_TIME_N:
			fc_svf->type = TIMETZOID;
			fc_svf->typmod = anytime_typmod_check(true, fc_svf->typmod);
			break;
		case SVFOP_CURRENT_TIMESTAMP:
			fc_svf->type = TIMESTAMPTZOID;
			break;
		case SVFOP_CURRENT_TIMESTAMP_N:
			fc_svf->type = TIMESTAMPTZOID;
			fc_svf->typmod = anytimestamp_typmod_check(true, fc_svf->typmod);
			break;
		case SVFOP_LOCALTIME:
			fc_svf->type = TIMEOID;
			break;
		case SVFOP_LOCALTIME_N:
			fc_svf->type = TIMEOID;
			fc_svf->typmod = anytime_typmod_check(false, fc_svf->typmod);
			break;
		case SVFOP_LOCALTIMESTAMP:
			fc_svf->type = TIMESTAMPOID;
			break;
		case SVFOP_LOCALTIMESTAMP_N:
			fc_svf->type = TIMESTAMPOID;
			fc_svf->typmod = anytimestamp_typmod_check(false, fc_svf->typmod);
			break;
		case SVFOP_CURRENT_ROLE:
		case SVFOP_CURRENT_USER:
		case SVFOP_USER:
		case SVFOP_SESSION_USER:
		case SVFOP_CURRENT_CATALOG:
		case SVFOP_CURRENT_SCHEMA:
			fc_svf->type = NAMEOID;
			break;
	}

	return (Node *) fc_svf;
}

static Node * fc_transformXmlExpr(ParseState *fc_pstate, XmlExpr *fc_x)
{
	XmlExpr    *fc_newx;
	ListCell   *fc_lc;
	int			fc_i;

	fc_newx = makeNode(XmlExpr);
	fc_newx->op = fc_x->op;
	if (fc_x->name)
		fc_newx->name = map_sql_identifier_to_xml_name(fc_x->name, false, false);
	else
		fc_newx->name = NULL;
	fc_newx->xmloption = fc_x->xmloption;
	fc_newx->type = XMLOID;		/* 这只是将节点标记为已转换 */
	fc_newx->typmod = -1;
	fc_newx->location = fc_x->location;

	/*
	 * gram.y 将命名参数构建为 ResTarget 的列表。转换每个，
	 * 并将名称分开为一个单独的列表。
	 */
	fc_newx->named_args = NIL;
	fc_newx->arg_names = NIL;

	foreach(fc_lc, fc_x->named_args)
	{
		ResTarget  *fc_r = lfirst_node(ResTarget, fc_lc);
		Node	   *fc_expr;
		char	   *fc_argname;

		fc_expr = fc_transformExprRecurse(fc_pstate, fc_r->val);

		if (fc_r->name)
			fc_argname = map_sql_identifier_to_xml_name(fc_r->name, false, false);
		else if (IsA(fc_r->val, ColumnRef))
			fc_argname = map_sql_identifier_to_xml_name(FigureColname(fc_r->val),
													 true, false);
		else
		{
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 fc_x->op == IS_XMLELEMENT
					 ? errmsg("unnamed XML attribute value must be a column reference")
					 : errmsg("unnamed XML element value must be a column reference"),
					 parser_errposition(fc_pstate, fc_r->location)));
			fc_argname = NULL;		/* 保持编译器安静 */
		}

		/* 仅在 XMLELEMENT 中拒绝重复的 argnames */
		if (fc_x->op == IS_XMLELEMENT)
		{
			ListCell   *fc_lc2;

			foreach(fc_lc2, fc_newx->arg_names)
			{
				if (strcmp(fc_argname, strVal(lfirst(fc_lc2))) == 0)
					ereport(ERROR,
							(errcode(ERRCODE_SYNTAX_ERROR),
							 errmsg("XML attribute name \"%s\" appears more than once",
									fc_argname),
							 parser_errposition(fc_pstate, fc_r->location)));
			}
		}

		fc_newx->named_args = lappend(fc_newx->named_args, fc_expr);
		fc_newx->arg_names = lappend(fc_newx->arg_names, makeString(fc_argname));
	}

	/* 其他参数的类型各不相同，具体取决于函数 */
	fc_newx->args = NIL;
	fc_i = 0;
	foreach(fc_lc, fc_x->args)
	{
		Node	   *fc_e = (Node *) lfirst(fc_lc);
		Node	   *fc_newe;

		fc_newe = fc_transformExprRecurse(fc_pstate, fc_e);
		switch (fc_x->op)
		{
			case IS_XMLCONCAT:
				fc_newe = coerce_to_specific_type(fc_pstate, fc_newe, XMLOID,
											   "XMLCONCAT");
				break;
			case IS_XMLELEMENT:
				/* 无需强制转换 */
				break;
			case IS_XMLFOREST:
				fc_newe = coerce_to_specific_type(fc_pstate, fc_newe, XMLOID,
											   "XMLFOREST");
				break;
			case IS_XMLPARSE:
				if (fc_i == 0)
					fc_newe = coerce_to_specific_type(fc_pstate, fc_newe, TEXTOID,
												   "XMLPARSE");
				else
					fc_newe = coerce_to_boolean(fc_pstate, fc_newe, "XMLPARSE");
				break;
			case IS_XMLPI:
				fc_newe = coerce_to_specific_type(fc_pstate, fc_newe, TEXTOID,
											   "XMLPI");
				break;
			case IS_XMLROOT:
				if (fc_i == 0)
					fc_newe = coerce_to_specific_type(fc_pstate, fc_newe, XMLOID,
												   "XMLROOT");
				else if (fc_i == 1)
					fc_newe = coerce_to_specific_type(fc_pstate, fc_newe, TEXTOID,
												   "XMLROOT");
				else
					fc_newe = coerce_to_specific_type(fc_pstate, fc_newe, INT4OID,
												   "XMLROOT");
				break;
			case IS_XMLSERIALIZE:
				/* 此处未处理 */
				Assert(false);
				break;
			case IS_DOCUMENT:
				fc_newe = coerce_to_specific_type(fc_pstate, fc_newe, XMLOID,
											   "IS DOCUMENT");
				break;
		}
		fc_newx->args = lappend(fc_newx->args, fc_newe);
		fc_i++;
	}

	return (Node *) fc_newx;
}

static Node * fc_transformXmlSerialize(ParseState *fc_pstate, XmlSerialize *fc_xs)
{
	Node	   *fc_result;
	XmlExpr    *fc_xexpr;
	Oid			fc_targetType;
	int32		fc_targetTypmod;

	fc_xexpr = makeNode(XmlExpr);
	fc_xexpr->op = IS_XMLSERIALIZE;
	fc_xexpr->args = list_make1(coerce_to_specific_type(fc_pstate,
													 fc_transformExprRecurse(fc_pstate, fc_xs->expr),
													 XMLOID,
													 "XMLSERIALIZE"));

	typenameTypeIdAndMod(fc_pstate, fc_xs->typeName, &fc_targetType, &fc_targetTypmod);

	fc_xexpr->xmloption = fc_xs->xmloption;
	fc_xexpr->location = fc_xs->location;
	/* 我们实际上只需要这些，以便能够解析表达式。 */
	fc_xexpr->type = fc_targetType;
	fc_xexpr->typmod = fc_targetTypmod;

	/*
	 * 实际目标类型是这样确定的。SQL 允许 char 和
	 * varchar 作为目标类型。我们允许任何可以从 text 隐式转换的类型。
	 * 这样，用户定义的类似文本的数据类型就可以自动适配。
	 */
	fc_result = coerce_to_target_type(fc_pstate, (Node *) fc_xexpr,
								   TEXTOID, fc_targetType, fc_targetTypmod,
								   COERCION_IMPLICIT,
								   COERCE_IMPLICIT_CAST,
								   -1);
	if (fc_result == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_CANNOT_COERCE),
				 errmsg("cannot cast XMLSERIALIZE result to %s",
						format_type_be(fc_targetType)),
				 parser_errposition(fc_pstate, fc_xexpr->location)));
	return fc_result;
}

static Node * fc_transformBooleanTest(ParseState *fc_pstate, BooleanTest *fc_b)
{
	const char *fc_clausename;

	switch (fc_b->booltesttype)
	{
		case IS_TRUE:
			fc_clausename = "IS TRUE";
			break;
		case IS_NOT_TRUE:
			fc_clausename = "IS NOT TRUE";
			break;
		case IS_FALSE:
			fc_clausename = "IS FALSE";
			break;
		case IS_NOT_FALSE:
			fc_clausename = "IS NOT FALSE";
			break;
		case IS_UNKNOWN:
			fc_clausename = "IS UNKNOWN";
			break;
		case IS_NOT_UNKNOWN:
			fc_clausename = "IS NOT UNKNOWN";
			break;
		default:
			elog(ERROR, "unrecognized booltesttype: %d",
				 (int) fc_b->booltesttype);
			fc_clausename = NULL;	/* 保持编译器安静 */
	}

	fc_b->arg = (Expr *) fc_transformExprRecurse(fc_pstate, (Node *) fc_b->arg);

	fc_b->arg = (Expr *) coerce_to_boolean(fc_pstate,
										(Node *) fc_b->arg,
										fc_clausename);

	return (Node *) fc_b;
}

static Node * fc_transformCurrentOfExpr(ParseState *fc_pstate, CurrentOfExpr *fc_cexpr)
{
	/* CURRENT OF 只能出现在 UPDATE/DELETE 的顶层 */
	Assert(fc_pstate->p_target_nsitem != NULL);
	fc_cexpr->cvarno = fc_pstate->p_target_nsitem->p_rtindex;

	/*
	 * 检查光标名称是否与 REFCURSOR 类型的参数匹配。
	 * 如果匹配，则用参数引用替换原始名称引用。 （这是为了方便 plpgsql 的一种黑客操作。）
	 */
	if (fc_cexpr->cursor_name != NULL) /* 如果已经转换 */
	{
		ColumnRef  *fc_cref = makeNode(ColumnRef);
		Node	   *fc_node = NULL;

		/* 使用给定名称构建 unqualified ColumnRef */
		fc_cref->fields = list_make1(makeString(fc_cexpr->cursor_name));
		fc_cref->location = -1;

		/* 查看是否有来自解析器 hook 的翻译可用 */
		if (fc_pstate->p_pre_columnref_hook != NULL)
			fc_node = fc_pstate->p_pre_columnref_hook(fc_pstate, fc_cref);
		if (fc_node == NULL && fc_pstate->p_post_columnref_hook != NULL)
			fc_node = fc_pstate->p_post_columnref_hook(fc_pstate, fc_cref, NULL);

		/*
		 * XXX 如果我们得到的翻译不是 refcursor Param，是否应该抛出错误？
		 * 目前看来，静默忽略错误匹配似乎是最好的选择。
		 */
		if (fc_node != NULL && IsA(fc_node, Param))
		{
			Param	   *fc_p = (Param *) fc_node;

			if (fc_p->paramkind == PARAM_EXTERN &&
				fc_p->paramtype == REFCURSOROID)
			{
				/* 匹配，因此将 CURRENT OF 转换为参数引用 */
				fc_cexpr->cursor_name = NULL;
				fc_cexpr->cursor_param = fc_p->paramid;
			}
		}
	}

	return (Node *) fc_cexpr;
}

/*
 * 构造一个整体行引用，以表示“relation.*”的表示法。
 */
static Node * fc_transformWholeRowRef(ParseState *fc_pstate, ParseNamespaceItem *fc_nsitem,
					 int fc_sublevels_up, int fc_location)
{
	/*
	 * 构建适当的引用节点。 通常这可以是一个
	 * 整体行 Var，但如果 nsitem 是 JOIN USING 别名，则它只包含
	 * 基础连接 RTE 的子集列，因此这样做将无效。
	 * 相反，我们立即将引用扩展为 RowExpr。
	 * 由于 JOIN USING 的公共列在此时已完全确定，因此在计划之前现在扩展它似乎没有坏处。
	 *
	 * 请注意，如果 RTE 是一个返回标量的函数，我们仅创建一个
	 * 普通引用函数值，而不是包含单个列的复合类型。
	 * 这乍一看似乎不太一致，但这就是我们历史上所做的。
	 * 一个理由是“rel”和“rel.*”对于复合关系有相同的含义，那么标量函数为何不呢...
	 */
	if (fc_nsitem->p_names == fc_nsitem->p_rte->eref)
	{
		Var		   *fc_result;

		fc_result = makeWholeRowVar(fc_nsitem->p_rte, fc_nsitem->p_rtindex,
								 fc_sublevels_up, true);

		/* location 不由 makeWholeRowVar 填充 */
		fc_result->location = fc_location;

		/* 将关系标记为需要整体行 SELECT 访问 */
		markVarForSelectPriv(fc_pstate, fc_result);

		return (Node *) fc_result;
	}
	else
	{
		RowExpr    *fc_rowexpr;
		List	   *fc_fields;

		/*
		 * 我们只想要 p_names->colnames 中列出的列数，
		 * 而且我们应该使用这些名称，而不是 RTE 中可能别名的名称。
		 * 我们不必担心为 SELECT 访问标记 RTE，
		 * 因为共同列肯定已经标记过了。
		 */
		expandRTE(fc_nsitem->p_rte, fc_nsitem->p_rtindex,
				  fc_sublevels_up, fc_location, false,
				  NULL, &fc_fields);
		fc_rowexpr = makeNode(RowExpr);
		fc_rowexpr->args = list_truncate(fc_fields,
									  list_length(fc_nsitem->p_names->colnames));
		fc_rowexpr->row_typeid = RECORDOID;
		fc_rowexpr->row_format = COERCE_IMPLICIT_CAST;
		fc_rowexpr->colnames = copyObject(fc_nsitem->p_names->colnames);
		fc_rowexpr->location = fc_location;

		return (Node *) fc_rowexpr;
	}
}

/*
 * 处理显式 CAST 构造。
 *
 * 转换参数，查找类型名称，并应用任何必要的
 * 强制转换函数。
 */
static Node * fc_transformTypeCast(ParseState *fc_pstate, TypeCast *fc_tc)
{
	Node	   *fc_result;
	Node	   *fc_arg = fc_tc->arg;
	Node	   *fc_expr;
	Oid			fc_inputType;
	Oid			fc_targetType;
	int32		fc_targetTypmod;
	int			fc_location;

	/* 首先查找类型名称 */
	typenameTypeIdAndMod(fc_pstate, fc_tc->typeName, &fc_targetType, &fc_targetTypmod);

	/*
	 * 如果类型转换的主题是一个 ARRAY[] 构造，目标
	 * 类型是数组类型，我们直接调用 transformArrayExpr()，
	 * 以便我们可以传递类型信息。这避免了某些情况下
	 * transformArrayExpr() 可能无法推断正确类型的问题。
	 * 否则，正常转换参数。
	 */
	if (IsA(fc_arg, A_ArrayExpr))
	{
		Oid			fc_targetBaseType;
		int32		fc_targetBaseTypmod;
		Oid			fc_elementType;

		/*
		 * 如果目标是数组上的域，这里使用基础数组类型。
		 * 下面，我们将数组类型转换为域。
		 * 在通常情况下，目标不是域，其余步骤将是无操作。
		 */
		fc_targetBaseTypmod = fc_targetTypmod;
		fc_targetBaseType = getBaseTypeAndTypmod(fc_targetType, &fc_targetBaseTypmod);
		fc_elementType = get_element_type(fc_targetBaseType);
		if (OidIsValid(fc_elementType))
		{
			fc_expr = fc_transformArrayExpr(fc_pstate,
									  (A_ArrayExpr *) fc_arg,
									  fc_targetBaseType,
									  fc_elementType,
									  fc_targetBaseTypmod);
		}
		else
			fc_expr = fc_transformExprRecurse(fc_pstate, fc_arg);
	}
	else
		fc_expr = fc_transformExprRecurse(fc_pstate, fc_arg);

	fc_inputType = exprType(fc_expr);
	if (fc_inputType == InvalidOid)
		return fc_expr;			/* 如果输入为空则不做任何事情 */

	/*
	 * 强制转换的位置优先于 :: 或 CAST 符号的位置，
	 * 但如果没有，则使用类型名称的位置（例如，在 TypeName 'string' 语法中可能发生）。
	 */
	fc_location = fc_tc->location;
	if (fc_location < 0)
		fc_location = fc_tc->typeName->location;

	fc_result = coerce_to_target_type(fc_pstate, fc_expr, fc_inputType,
								   fc_targetType, fc_targetTypmod,
								   COERCION_EXPLICIT,
								   COERCE_EXPLICIT_CAST,
								   fc_location);
	if (fc_result == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_CANNOT_COERCE),
				 errmsg("cannot cast type %s to %s",
						format_type_be(fc_inputType),
						format_type_be(fc_targetType)),
				 parser_coercion_errposition(fc_pstate, fc_location, fc_expr)));

	return fc_result;
}

/*
 * 处理显式 COLLATE 子句。
 *
 * 转换参数，并查找排序名称。
 */
static Node * fc_transformCollateClause(ParseState *fc_pstate, CollateClause *fc_c)
{
	CollateExpr *fc_newc;
	Oid			fc_argtype;

	fc_newc = makeNode(CollateExpr);
	fc_newc->arg = (Expr *) fc_transformExprRecurse(fc_pstate, fc_c->arg);

	fc_argtype = exprType((Node *) fc_newc->arg);

	/*
	 * 未知类型不能进行排序比较，但是 coerce_type() 会单独处理，
	 * 所以我们在这里就让它过去。
	 */
	if (!type_is_collatable(fc_argtype) && fc_argtype != UNKNOWNOID)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("collations are not supported by type %s",
						format_type_be(fc_argtype)),
				 parser_errposition(fc_pstate, fc_c->location)));

	fc_newc->collOid = LookupCollation(fc_pstate, fc_c->collname, fc_c->location);
	fc_newc->location = fc_c->location;

	return (Node *) fc_newc;
}

/*
 * 转换“行比较运算符行”构造
 *
 * 输入是已转换表达式的列表。
 * 与 coerce_type 一样，如果不需要特殊的未知参数处理，
 * pstate 可能为 NULL。
 *
 * 输出可能是单个 OpExpr、一系列 OpExpr 的 AND 或 OR 组合，
 * 或 RowCompareExpr。在所有情况下，它保证返回布尔值。
 * AND、OR 和 RowCompareExpr 情况进一步暗示了运算符的行为
 * （即，它们表现为 =、<> 或 < <= > >=）。
 */
static Node * fc_make_row_comparison_op(ParseState *fc_pstate, List *fc_opname,
					   List *fc_largs, List *fc_rargs, int fc_location)
{
	RowCompareExpr *fc_rcexpr;
	RowCompareType fc_rctype;
	List	   *fc_opexprs;
	List	   *fc_opnos;
	List	   *fc_opfamilies;
	ListCell   *fc_l,
			   *fc_r;
	List	  **fc_opinfo_lists;
	Bitmapset  *fc_strats;
	int			fc_nopers;
	int			fc_i;

	fc_nopers = list_length(fc_largs);
	if (fc_nopers != list_length(fc_rargs))
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("unequal number of entries in row expressions"),
				 parser_errposition(fc_pstate, fc_location)));

	/*
	 * 我们不能比较零长度的行，因为没有原则依据来
	 * 判断运算符是什么。
	 */
	if (fc_nopers == 0)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot compare rows of zero length"),
				 parser_errposition(fc_pstate, fc_location)));

	/*
	 * 使用 make_op 识别所有成对运算符，以确保与简单标量情况的行为相同。
	 */
	fc_opexprs = NIL;
	forboth(fc_l, fc_largs, fc_r, fc_rargs)
	{
		Node	   *fc_larg = (Node *) lfirst(fc_l);
		Node	   *fc_rarg = (Node *) lfirst(fc_r);
		OpExpr	   *fc_cmp;

		fc_cmp = castNode(OpExpr, make_op(fc_pstate, fc_opname, fc_larg, fc_rarg,
									   fc_pstate->p_last_srf, fc_location));

		/*
		 * 我们在这里不使用 coerce_to_boolean，因为我们坚持运算符直接
		 * 产生布尔值，而不是通过强制转换。如果它没有产生布尔值，
		 * 它不会出现在任何索引运算符族中...
		 */
		if (fc_cmp->opresulttype != BOOLOID)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("row comparison operator must yield type boolean, "
							"not type %s",
							format_type_be(fc_cmp->opresulttype)),
					 parser_errposition(fc_pstate, fc_location)));
		if (expression_returns_set((Node *) fc_cmp))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("row comparison operator must not return a set"),
					 parser_errposition(fc_pstate, fc_location)));
		fc_opexprs = lappend(fc_opexprs, fc_cmp);
	}

	/*
	 * 如果行长度为 1，只需返回单个运算符。在这种情况下，
	 * 我们不坚持识别运算符的 btree 语义（但我们仍然要求它返回布尔值）。
	 */
	if (fc_nopers == 1)
		return (Node *) linitial(fc_opexprs);

	/*
	 * 现在我们必须确定哪些行比较语义（= <> < <= > >=）
	 * 适用于这一组运算符。我们查找包含运算符的 btree 运算符族，
	 * 并查看每个运算符的解释（策略编号）有哪些。
	 */
	fc_opinfo_lists = (List **) palloc(fc_nopers * sizeof(List *));
	fc_strats = NULL;
	fc_i = 0;
	foreach(fc_l, fc_opexprs)
	{
		Oid			fc_opno = ((OpExpr *) lfirst(fc_l))->opno;
		Bitmapset  *fc_this_strats;
		ListCell   *fc_j;

		fc_opinfo_lists[fc_i] = get_op_btree_interpretation(fc_opno);

		/*
		 * 将策略编号转换为 Bitmapset，以便使交集计算变得简单。
		 */
		fc_this_strats = NULL;
		foreach(fc_j, fc_opinfo_lists[fc_i])
		{
			OpBtreeInterpretation *fc_opinfo = lfirst(fc_j);

			fc_this_strats = bms_add_member(fc_this_strats, fc_opinfo->strategy);
		}
		if (fc_i == 0)
			fc_strats = fc_this_strats;
		else
			fc_strats = bms_int_members(fc_strats, fc_this_strats);
		fc_i++;
	}

	/*
	 * 如果有多个常见解释，我们可以使用其中任何一个...
	 * 此编码任意选择最低的 btree 策略编号。
	 */
	fc_i = bms_first_member(fc_strats);
	if (fc_i < 0)
	{
		/* 没有共同解释，因此失败 */
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("could not determine interpretation of row comparison operator %s",
						strVal(llast(fc_opname))),
				 errhint("Row comparison operators must be associated with btree operator families."),
				 parser_errposition(fc_pstate, fc_location)));
	}
	fc_rctype = (RowCompareType) fc_i;

	/*
	 * 对于 = 和 <> 情况，我们只需分别使用 AND 或 OR 组合成对运算符。
	 */
	if (fc_rctype == ROWCOMPARE_EQ)
		return (Node *) makeBoolExpr(AND_EXPR, fc_opexprs, fc_location);
	if (fc_rctype == ROWCOMPARE_NE)
		return (Node *) makeBoolExpr(OR_EXPR, fc_opexprs, fc_location);

	/*
	 * 否则，我们需要准确选择哪个运算符族与每个运算符关联。
	 */
	fc_opfamilies = NIL;
	for (fc_i = 0; fc_i < fc_nopers; fc_i++)
	{
		Oid			fc_opfamily = InvalidOid;
		ListCell   *fc_j;

		foreach(fc_j, fc_opinfo_lists[fc_i])
		{
			OpBtreeInterpretation *fc_opinfo = lfirst(fc_j);

			if (fc_opinfo->strategy == fc_rctype)
			{
				fc_opfamily = fc_opinfo->opfamily_id;
				break;
			}
		}
		if (OidIsValid(fc_opfamily))
			fc_opfamilies = lappend_oid(fc_opfamilies, fc_opfamily);
		else					/* 不应该发生 */
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("could not determine interpretation of row comparison operator %s",
							strVal(llast(fc_opname))),
					 errdetail("There are multiple equally-plausible candidates."),
					 parser_errposition(fc_pstate, fc_location)));
	}

	/*
	 * 现在解构 OpExpr 并创建 RowCompareExpr。
	 *
	 * 注意：不能简单地重用传入的 largs/rargs 列表，
	 * 因为可能 make_op 插入了强制转换操作。
	 */
	fc_opnos = NIL;
	fc_largs = NIL;
	fc_rargs = NIL;
	foreach(fc_l, fc_opexprs)
	{
		OpExpr	   *fc_cmp = (OpExpr *) lfirst(fc_l);

		fc_opnos = lappend_oid(fc_opnos, fc_cmp->opno);
		fc_largs = lappend(fc_largs, linitial(fc_cmp->args));
		fc_rargs = lappend(fc_rargs, lsecond(fc_cmp->args));
	}

	fc_rcexpr = makeNode(RowCompareExpr);
	fc_rcexpr->rctype = fc_rctype;
	fc_rcexpr->opnos = fc_opnos;
	fc_rcexpr->opfamilies = fc_opfamilies;
	fc_rcexpr->inputcollids = NIL; /* assign_expr_collations 将解决此问题 */
	fc_rcexpr->largs = fc_largs;
	fc_rcexpr->rargs = fc_rargs;

	return (Node *) fc_rcexpr;
}

/*
 * 转换“行 IS DISTINCT FROM 行”构造
 *
 * 输入的 RowExprs 已经转换
 */
static Node * fc_make_row_distinct_op(ParseState *fc_pstate, List *fc_opname,
					 RowExpr *fc_lrow, RowExpr *fc_rrow,
					 int fc_location)
{
	Node	   *fc_result = NULL;
	List	   *fc_largs = fc_lrow->args;
	List	   *fc_rargs = fc_rrow->args;
	ListCell   *fc_l,
			   *fc_r;

	if (list_length(fc_largs) != list_length(fc_rargs))
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("unequal number of entries in row expressions"),
				 parser_errposition(fc_pstate, fc_location)));

	forboth(fc_l, fc_largs, fc_r, fc_rargs)
	{
		Node	   *fc_larg = (Node *) lfirst(fc_l);
		Node	   *fc_rarg = (Node *) lfirst(fc_r);
		Node	   *fc_cmp;

		fc_cmp = (Node *) fc_make_distinct_op(fc_pstate, fc_opname, fc_larg, fc_rarg, fc_location);
		if (fc_result == NULL)
			fc_result = fc_cmp;
		else
			fc_result = (Node *) makeBoolExpr(OR_EXPR,
										   list_make2(fc_result, fc_cmp),
										   fc_location);
	}

	if (fc_result == NULL)
	{
		/* 长度为零的行？生成常量 FALSE */
		fc_result = makeBoolConst(false, false);
	}

	return fc_result;
}

/*
 * 为 IS DISTINCT FROM 运算符生成节点
 */
static Expr * fc_make_distinct_op(ParseState *fc_pstate, List *fc_opname, Node *fc_ltree, Node *fc_rtree,
				 int fc_location)
{
	Expr	   *fc_result;

	fc_result = make_op(fc_pstate, fc_opname, fc_ltree, fc_rtree,
					 fc_pstate->p_last_srf, fc_location);
	if (((OpExpr *) fc_result)->opresulttype != BOOLOID)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("IS DISTINCT FROM requires = operator to yield boolean"),
				 parser_errposition(fc_pstate, fc_location)));
	if (((OpExpr *) fc_result)->opretset)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
		/* 翻译：%s 是 SQL 构造的名称，例如 NULLIF */
				 errmsg("%s must not return a set", "IS DISTINCT FROM"),
				 parser_errposition(fc_pstate, fc_location)));

	/*
	 * 我们依赖 DistinctExpr 和 OpExpr 是同一结构
	 */
	NodeSetTag(fc_result, T_DistinctExpr);

	return fc_result;
}

/*
 * 从 IS [NOT] DISTINCT FROM NULL 构造生成 NullTest 节点
 *
 * "arg" 是未转换的另一个参数
 */
static Node * fc_make_nulltest_from_distinct(ParseState *fc_pstate, A_Expr *fc_distincta, Node *fc_arg)
{
	NullTest   *fc_nt = makeNode(NullTest);

	fc_nt->arg = (Expr *) fc_transformExprRecurse(fc_pstate, fc_arg);
	/* 参数可以是任何类型，因此不要强制转换 */
	if (fc_distincta->kind == AEXPR_NOT_DISTINCT)
		fc_nt->nulltesttype = IS_NULL;
	else
		fc_nt->nulltesttype = IS_NOT_NULL;
	/* argisrow = false 无论 arg 是否为复合类型都是正确的 */
	fc_nt->argisrow = false;
	fc_nt->location = fc_distincta->location;
	return (Node *) fc_nt;
}

/*
 * 生成一个识别表达式类型的字符串。
 *
 * 注意：在可行的情况下，使用一个简单的 SQL 关键字作为结果。
 * 如果这样不合适，请检查调用位置以查看是否需要自定义错误消息字符串。
 */
const char * ParseExprKindName(ParseExprKind fc_exprKind)
{
	switch (fc_exprKind)
	{
		case EXPR_KIND_NONE:
			return "invalid expression context";
		case EXPR_KIND_OTHER:
			return "extension expression";
		case EXPR_KIND_JOIN_ON:
			return "JOIN/ON";
		case EXPR_KIND_JOIN_USING:
			return "JOIN/USING";
		case EXPR_KIND_FROM_SUBSELECT:
			return "sub-SELECT in FROM";
		case EXPR_KIND_FROM_FUNCTION:
			return "function in FROM";
		case EXPR_KIND_WHERE:
			return "WHERE";
		case EXPR_KIND_POLICY:
			return "POLICY";
		case EXPR_KIND_HAVING:
			return "HAVING";
		case EXPR_KIND_FILTER:
			return "FILTER";
		case EXPR_KIND_WINDOW_PARTITION:
			return "window PARTITION BY";
		case EXPR_KIND_WINDOW_ORDER:
			return "window ORDER BY";
		case EXPR_KIND_WINDOW_FRAME_RANGE:
			return "window RANGE";
		case EXPR_KIND_WINDOW_FRAME_ROWS:
			return "window ROWS";
		case EXPR_KIND_WINDOW_FRAME_GROUPS:
			return "window GROUPS";
		case EXPR_KIND_SELECT_TARGET:
			return "SELECT";
		case EXPR_KIND_INSERT_TARGET:
			return "INSERT";
		case EXPR_KIND_UPDATE_SOURCE:
		case EXPR_KIND_UPDATE_TARGET:
			return "UPDATE";
		case EXPR_KIND_MERGE_WHEN:
			return "MERGE WHEN";
		case EXPR_KIND_GROUP_BY:
			return "GROUP BY";
		case EXPR_KIND_ORDER_BY:
			return "ORDER BY";
		case EXPR_KIND_DISTINCT_ON:
			return "DISTINCT ON";
		case EXPR_KIND_LIMIT:
			return "LIMIT";
		case EXPR_KIND_OFFSET:
			return "OFFSET";
		case EXPR_KIND_RETURNING:
			return "RETURNING";
		case EXPR_KIND_VALUES:
		case EXPR_KIND_VALUES_SINGLE:
			return "VALUES";
		case EXPR_KIND_CHECK_CONSTRAINT:
		case EXPR_KIND_DOMAIN_CHECK:
			return "CHECK";
		case EXPR_KIND_COLUMN_DEFAULT:
		case EXPR_KIND_FUNCTION_DEFAULT:
			return "DEFAULT";
		case EXPR_KIND_INDEX_EXPRESSION:
			return "index expression";
		case EXPR_KIND_INDEX_PREDICATE:
			return "index predicate";
		case EXPR_KIND_STATS_EXPRESSION:
			return "statistics expression";
		case EXPR_KIND_ALTER_COL_TRANSFORM:
			return "USING";
		case EXPR_KIND_EXECUTE_PARAMETER:
			return "EXECUTE";
		case EXPR_KIND_TRIGGER_WHEN:
			return "WHEN";
		case EXPR_KIND_PARTITION_BOUND:
			return "partition bound";
		case EXPR_KIND_PARTITION_EXPRESSION:
			return "PARTITION BY";
		case EXPR_KIND_CALL_ARGUMENT:
			return "CALL";
		case EXPR_KIND_COPY_WHERE:
			return "WHERE";
		case EXPR_KIND_GENERATED_COLUMN:
			return "GENERATED AS";
		case EXPR_KIND_CYCLE_MARK:
			return "CYCLE";

			/*
			 * 此处故意没有默认 case，以便
			 * 如果我们添加一个新的 ParseExprKind 而不扩展此开关，
			 * 编译器将会发出警告。如果运行时看到一个无法识别的值，
			 * 我们将通过“未识别”的返回值继续。
			 */
	}
	return "unrecognized expression kind";
}
